Skip to main content

snarkvm_console_program/response/
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
16use crate::{DynamicFuture, DynamicRecord, Identifier, ProgramID, Register, Value, ValueType, compute_function_id};
17use snarkvm_console_network::Network;
18use snarkvm_console_types::prelude::*;
19
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum OutputID<N: Network> {
22    /// The hash of the constant output.
23    Constant(Field<N>),
24    /// The hash of the public output.
25    Public(Field<N>),
26    /// The ciphertext hash of the private output.
27    Private(Field<N>),
28    /// The `(commitment, checksum, sender_ciphertext)` tuple of the record output.
29    Record(Field<N>, Field<N>, Field<N>),
30    /// The hash of the external record's (function_id, record, tvk, output index).
31    ExternalRecord(Field<N>),
32    /// The hash of the future output.
33    Future(Field<N>),
34    /// The hash of the dynamic record's (function_id, dynamic record, tvk, output index).
35    DynamicRecord(Field<N>),
36    /// The hash of the dynamic future's (function_id, dynamic future, tcm, output index).
37    DynamicFuture(Field<N>),
38}
39
40impl<N: Network> OutputID<N> {
41    /// Returns the (primary) output ID.
42    pub const fn id(&self) -> &Field<N> {
43        match self {
44            OutputID::Constant(id) => id,
45            OutputID::Public(id) => id,
46            OutputID::Private(id) => id,
47            OutputID::Record(id, ..) => id,
48            OutputID::ExternalRecord(id) => id,
49            OutputID::Future(id) => id,
50            OutputID::DynamicRecord(id) => id,
51            OutputID::DynamicFuture(id) => id,
52        }
53    }
54
55    /// Computes the output ID for a constant output.
56    /// Constructs the preimage as `(function_id || output || tcm || index)` and hashes it.
57    pub fn constant(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
58        // Ensure the output is a plaintext.
59        ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
60        // Construct the (console) output index as a field element.
61        let index = Field::from_u16(index);
62        // Construct the preimage as `(function ID || output || tcm || index)`.
63        let mut preimage = Vec::new();
64        preimage.push(function_id);
65        preimage.extend(output.to_fields()?);
66        preimage.push(tcm);
67        preimage.push(index);
68        // Hash the output to a field element.
69        let hash = N::hash_psd8(&preimage)?;
70        Ok(Self::Constant(hash))
71    }
72
73    /// Computes the output ID for a public output.
74    /// Constructs the preimage as `(function_id || output || tcm || index)` and hashes it.
75    pub fn public(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
76        // Ensure the output is a plaintext.
77        ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
78        // Construct the (console) output index as a field element.
79        let index = Field::from_u16(index);
80        // Construct the preimage as `(function ID || output || tcm || index)`.
81        let mut preimage = Vec::new();
82        preimage.push(function_id);
83        preimage.extend(output.to_fields()?);
84        preimage.push(tcm);
85        preimage.push(index);
86        // Hash the output to a field element.
87        let hash = N::hash_psd8(&preimage)?;
88        Ok(Self::Public(hash))
89    }
90
91    /// Computes the output ID for a private output.
92    /// Encrypts the output using the output view key derived from `tvk` and hashes the ciphertext.
93    pub fn private(function_id: Field<N>, output: &Value<N>, tvk: Field<N>, index: u16) -> Result<Self> {
94        // Ensure the output is a plaintext.
95        ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
96        // Construct the (console) output index as a field element.
97        let index = Field::from_u16(index);
98        // Compute the output view key as `Hash(function ID || tvk || index)`.
99        let output_view_key = N::hash_psd4(&[function_id, tvk, index])?;
100        // Compute the ciphertext.
101        let ciphertext = match output {
102            Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key)?,
103            Value::Record(..) => bail!("Expected a plaintext output, found a record output"),
104            Value::Future(..) => bail!("Expected a plaintext output, found a future output"),
105            Value::DynamicRecord(..) => bail!("Expected a plaintext output, found a dynamic record output"),
106            Value::DynamicFuture(..) => bail!("Expected a plaintext output, found a dynamic future output"),
107        };
108        // Hash the ciphertext to a field element.
109        let hash = N::hash_psd8(&ciphertext.to_fields()?)?;
110        Ok(Self::Private(hash))
111    }
112
113    /// Computes the output ID for a record output.
114    /// Encrypts the record using `tvk` and returns the `(commitment, checksum, sender_ciphertext)` tuple.
115    pub fn record(
116        signer: &Address<N>,
117        program_id: &ProgramID<N>,
118        record_name: &Identifier<N>,
119        output: &Value<N>,
120        tvk: Field<N>,
121        output_register: &Register<N>,
122    ) -> Result<Self> {
123        // Retrieve the record.
124        let record = match output {
125            Value::Record(record) => record,
126            Value::Plaintext(..) => bail!("Expected a record output, found a plaintext output"),
127            Value::Future(..) => bail!("Expected a record output, found a future output"),
128            Value::DynamicRecord(..) => bail!("Expected a record output, found a dynamic record output"),
129            Value::DynamicFuture(..) => bail!("Expected a record output, found a dynamic future output"),
130        };
131        // Construct the (console) output index as a field element.
132        let index = Field::from_u64(output_register.locator());
133        // Compute the encryption randomizer as `HashToScalar(tvk || index)`.
134        let randomizer = N::hash_to_scalar_psd2(&[tvk, index])?;
135        // Encrypt the record, using the randomizer.
136        let (encrypted_record, record_view_key) = record.encrypt_symmetric(randomizer)?;
137        // Compute the record commitment.
138        let commitment = record.to_commitment(program_id, record_name, &record_view_key)?;
139        // Compute the record checksum, as the hash of the encrypted record.
140        let checksum = N::hash_bhp1024(&encrypted_record.to_bits_le())?;
141        // Prepare a randomizer for the sender ciphertext.
142        let randomizer = N::hash_psd4(&[N::encryption_domain(), record_view_key, Field::one()])?;
143        // Encrypt the signer address using the randomizer.
144        let sender_ciphertext = (**signer).to_x_coordinate() + randomizer;
145        Ok(Self::Record(commitment, checksum, sender_ciphertext))
146    }
147
148    /// Computes the output ID for an external record output.
149    /// Constructs the preimage as `(function_id || output || tvk || index)` and hashes it.
150    pub fn external_record(function_id: Field<N>, output: &Value<N>, tvk: Field<N>, index: u16) -> Result<Self> {
151        // Ensure the output is a record.
152        ensure!(matches!(output, Value::Record(..)), "Expected a record output");
153        // Construct the (console) output index as a field element.
154        let index = Field::from_u16(index);
155        // Construct the preimage as `(function ID || output || tvk || index)`.
156        let mut preimage = Vec::new();
157        preimage.push(function_id);
158        preimage.extend(output.to_fields()?);
159        preimage.push(tvk);
160        preimage.push(index);
161        // Hash the output to a field element.
162        let hash = N::hash_psd8(&preimage)?;
163        Ok(Self::ExternalRecord(hash))
164    }
165
166    /// Computes the output ID for a future output.
167    /// Constructs the preimage as `(function_id || output || tcm || index)` and hashes it.
168    pub fn future(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
169        // Ensure the output is a future.
170        ensure!(matches!(output, Value::Future(..)), "Expected a future output");
171        // Construct the (console) output index as a field element.
172        let index = Field::from_u16(index);
173        // Construct the preimage as `(function ID || output || tcm || index)`.
174        let mut preimage = Vec::new();
175        preimage.push(function_id);
176        preimage.extend(output.to_fields()?);
177        preimage.push(tcm);
178        preimage.push(index);
179        // Hash the output to a field element.
180        let hash = N::hash_psd8(&preimage)?;
181        Ok(Self::Future(hash))
182    }
183
184    /// Computes the output ID for a dynamic record output.
185    /// Constructs the preimage as `(function_id || output || tvk || index)` and hashes it.
186    pub fn dynamic_record(function_id: Field<N>, output: &Value<N>, tvk: Field<N>, index: u16) -> Result<Self> {
187        // Ensure the output is a dynamic record.
188        ensure!(matches!(output, Value::DynamicRecord(..)), "Expected a dynamic record output");
189        // Construct the (console) output index as a field element.
190        let index = Field::from_u16(index);
191        // Construct the preimage as `(function ID || output || tvk || index)`.
192        let mut preimage = Vec::new();
193        preimage.push(function_id);
194        preimage.extend(output.to_fields()?);
195        preimage.push(tvk);
196        preimage.push(index);
197        // Hash the output to a field element.
198        let hash = N::hash_psd8(&preimage)?;
199        Ok(Self::DynamicRecord(hash))
200    }
201
202    /// Computes the output ID for a dynamic future output.
203    /// Constructs the preimage as `(function_id || output || tcm || index)` and hashes it.
204    pub fn dynamic_future(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
205        // Ensure the output is a dynamic future.
206        ensure!(matches!(output, Value::DynamicFuture(..)), "Expected a dynamic future output");
207        // Construct the (console) output index as a field element.
208        let index = Field::from_u16(index);
209        // Construct the preimage as `(function ID || output || tcm || index)`.
210        let mut preimage = Vec::new();
211        preimage.push(function_id);
212        preimage.extend(output.to_fields()?);
213        preimage.push(tcm);
214        preimage.push(index);
215        // Hash the output to a field element.
216        let hash = N::hash_psd8(&preimage)?;
217        Ok(Self::DynamicFuture(hash))
218    }
219}
220
221#[derive(Clone, Debug, PartialEq, Eq)]
222pub struct Response<N: Network> {
223    /// The output ID for the transition.
224    output_ids: Vec<OutputID<N>>,
225    /// The function outputs.
226    outputs: Vec<Value<N>>,
227}
228
229impl<N: Network> From<(Vec<OutputID<N>>, Vec<Value<N>>)> for Response<N> {
230    /// Note: This method is used to eject from a circuit.
231    fn from((output_ids, outputs): (Vec<OutputID<N>>, Vec<Value<N>>)) -> Self {
232        Self { output_ids, outputs }
233    }
234}
235
236impl<N: Network> Response<N> {
237    /// Initializes a new response.
238    pub fn new(
239        signer: &Address<N>,
240        network_id: &U16<N>,
241        program_id: &ProgramID<N>,
242        function_name: &Identifier<N>,
243        num_inputs: usize,
244        tvk: &Field<N>,
245        tcm: &Field<N>,
246        outputs: Vec<Value<N>>,
247        output_types: &[ValueType<N>],
248        output_operands: &[Option<Register<N>>],
249    ) -> Result<Self> {
250        // Compute the function ID.
251        let function_id = compute_function_id(network_id, program_id, function_name)?;
252
253        // Compute the output IDs.
254        let output_ids = outputs
255            .iter()
256            .zip_eq(output_types)
257            .zip_eq(output_operands)
258            .enumerate()
259            .map(|(index, ((output, output_type), output_register))| {
260                match output_type {
261                    // For a constant output, compute the hash (using `tcm`) of the output.
262                    ValueType::Constant(..) => {
263                        let output_index =
264                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
265                        OutputID::constant(function_id, output, *tcm, output_index)
266                    }
267                    // For a public output, compute the hash (using `tcm`) of the output.
268                    ValueType::Public(..) => {
269                        let output_index =
270                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
271                        OutputID::public(function_id, output, *tcm, output_index)
272                    }
273                    // For a private output, encrypt (using `tvk`) and hash the ciphertext.
274                    ValueType::Private(..) => {
275                        let output_index =
276                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
277                        OutputID::private(function_id, output, *tvk, output_index)
278                    }
279                    // For a record output, compute the commitment and encrypt the record (using `tvk`).
280                    ValueType::Record(record_name) => {
281                        let Some(output_register) = output_register else {
282                            bail!("Expected a register to be paired with a record output");
283                        };
284                        OutputID::record(signer, program_id, record_name, output, *tvk, output_register)
285                    }
286                    // For an external record, compute the hash (using `tvk`) of the output.
287                    ValueType::ExternalRecord(..) => {
288                        let output_index =
289                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
290                        OutputID::external_record(function_id, output, *tvk, output_index)
291                    }
292                    // For a future output, compute the hash (using `tcm`) of the output.
293                    ValueType::Future(..) => {
294                        let output_index =
295                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
296                        OutputID::future(function_id, output, *tcm, output_index)
297                    }
298                    // For a dynamic record, compute the hash (using `tvk`) of the output.
299                    ValueType::DynamicRecord => {
300                        // Safe: num_inputs ≤ N::MAX_INPUTS and index < N::MAX_OUTPUTS,
301                        // so num_inputs + index fits well within u16::MAX.
302                        let output_index =
303                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
304                        OutputID::dynamic_record(function_id, output, *tvk, output_index)
305                    }
306                    // For a dynamic future output, compute the hash (using `tcm`) of the output.
307                    ValueType::DynamicFuture => {
308                        // Safe: num_inputs ≤ N::MAX_INPUTS and index < N::MAX_OUTPUTS,
309                        // so num_inputs + index fits well within u16::MAX.
310                        let output_index =
311                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
312                        OutputID::dynamic_future(function_id, output, *tcm, output_index)
313                    }
314                }
315            })
316            .collect::<Result<Vec<_>>>()?;
317
318        Ok(Self { output_ids, outputs })
319    }
320
321    /// Returns the output ID for the transition.
322    pub fn output_ids(&self) -> &[OutputID<N>] {
323        &self.output_ids
324    }
325
326    /// Returns the function outputs.
327    pub fn outputs(&self) -> &[Value<N>] {
328        &self.outputs
329    }
330
331    /// Returns the expected caller outputs for a dynamic call by:
332    /// - converting all record outputs to dynamic record outputs
333    /// - converting all future outputs to dynamic future outputs.
334    /// - leaving all other outputs unchanged.
335    pub fn to_dynamic_outputs(&self) -> Result<Vec<Value<N>>> {
336        self.outputs
337            .iter()
338            .map(|output| match output {
339                Value::Record(record) => {
340                    // This covers both the non-external and external record cases.
341                    Ok(Value::DynamicRecord(DynamicRecord::from_record(record)?))
342                }
343                Value::Future(future) => Ok(Value::DynamicFuture(DynamicFuture::from_future(future)?)),
344                Value::DynamicFuture(_) => bail!("A dynamic future cannot be a response output"),
345                Value::Plaintext(_) => Ok(output.clone()),
346                Value::DynamicRecord(_) => Ok(output.clone()),
347            })
348            .collect::<Result<Vec<_>>>()
349    }
350
351    /// Returns the expected caller output IDs for a dynamic call by:
352    /// - converting all record output IDs to dynamic record output IDs
353    /// - converting all future output IDs to dynamic future output IDs.
354    /// - leaving all other output IDs unchanged.
355    pub fn to_dynamic_output_ids(
356        &self,
357        network_id: &U16<N>,
358        program_id: &ProgramID<N>,
359        function_name: &Identifier<N>,
360        num_inputs: usize,
361        tvk: &Field<N>,
362        tcm: &Field<N>,
363    ) -> Result<Vec<OutputID<N>>> {
364        // Compute the function ID.
365        let function_id = compute_function_id(network_id, program_id, function_name)?;
366        // Get the caller outputs.
367        let caller_outputs = self.to_dynamic_outputs()?;
368        // Ensure the number of caller outputs matches the number of output IDs.
369        ensure!(
370            caller_outputs.len() == self.output_ids.len(),
371            "The number of caller outputs ({}) does not match the number of output IDs ({})",
372            caller_outputs.len(),
373            self.output_ids.len()
374        );
375        // Compute the caller output IDs for the caller outputs.
376        caller_outputs
377            .iter()
378            .zip_eq(self.output_ids.iter())
379            .enumerate()
380            .map(|(index, (output, callee_output_id))| {
381                match callee_output_id {
382                    OutputID::Record(_, _, _) | OutputID::ExternalRecord(_) => {
383                        let output_index =
384                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
385                        OutputID::dynamic_record(function_id, output, *tvk, output_index)
386                    }
387                    OutputID::Future(_) => {
388                        let output_index =
389                            u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16");
390                        OutputID::dynamic_future(function_id, output, *tcm, output_index)
391                    }
392                    // Otherwise, return the output ID unchanged.
393                    OutputID::Constant(_) => Ok(callee_output_id.clone()),
394                    OutputID::Public(_) => Ok(callee_output_id.clone()),
395                    OutputID::Private(_) => Ok(callee_output_id.clone()),
396                    OutputID::DynamicRecord(_) => Ok(callee_output_id.clone()),
397                    OutputID::DynamicFuture(_) => Ok(callee_output_id.clone()),
398                }
399            })
400            .collect::<Result<Vec<_>>>()
401    }
402}