snarkvm_console_program/response/mod.rs
1// Copyright 2024 Aleo Network Foundation
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::{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)` tuple of the record output.
29 Record(Field<N>, Field<N>),
30 /// The hash of the external record output.
31 ExternalRecord(Field<N>),
32 /// The hash of the future output.
33 Future(Field<N>),
34}
35
36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct Response<N: Network> {
38 /// The output ID for the transition.
39 output_ids: Vec<OutputID<N>>,
40 /// The function outputs.
41 outputs: Vec<Value<N>>,
42}
43
44impl<N: Network> From<(Vec<OutputID<N>>, Vec<Value<N>>)> for Response<N> {
45 /// Note: This method is used to eject from a circuit.
46 fn from((output_ids, outputs): (Vec<OutputID<N>>, Vec<Value<N>>)) -> Self {
47 Self { output_ids, outputs }
48 }
49}
50
51impl<N: Network> Response<N> {
52 /// Initializes a new response.
53 pub fn new(
54 network_id: &U16<N>,
55 program_id: &ProgramID<N>,
56 function_name: &Identifier<N>,
57 num_inputs: usize,
58 tvk: &Field<N>,
59 tcm: &Field<N>,
60 outputs: Vec<Value<N>>,
61 output_types: &[ValueType<N>],
62 output_operands: &[Option<Register<N>>],
63 ) -> Result<Self> {
64 // Compute the function ID.
65 let function_id = compute_function_id(network_id, program_id, function_name)?;
66
67 // Compute the output IDs.
68 let output_ids = outputs
69 .iter()
70 .zip_eq(output_types)
71 .zip_eq(output_operands)
72 .enumerate()
73 .map(|(index, ((output, output_type), output_register))| {
74 match output_type {
75 // For a constant output, compute the hash (using `tcm`) of the output.
76 ValueType::Constant(..) => {
77 // Ensure the output is a plaintext.
78 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
79
80 // Construct the (console) output index as a field element.
81 let index = Field::from_u16(
82 u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
83 );
84 // Construct the preimage as `(function ID || output || tcm || index)`.
85 let mut preimage = Vec::new();
86 preimage.push(function_id);
87 preimage.extend(output.to_fields()?);
88 preimage.push(*tcm);
89 preimage.push(index);
90 // Hash the output to a field element.
91 let output_hash = N::hash_psd8(&preimage)?;
92
93 // Return the output ID.
94 Ok(OutputID::Constant(output_hash))
95 }
96 // For a public output, compute the hash (using `tcm`) of the output.
97 ValueType::Public(..) => {
98 // Ensure the output is a plaintext.
99 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
100
101 // Construct the (console) output index as a field element.
102 let index = Field::from_u16(
103 u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
104 );
105 // Construct the preimage as `(function ID || output || tcm || index)`.
106 let mut preimage = Vec::new();
107 preimage.push(function_id);
108 preimage.extend(output.to_fields()?);
109 preimage.push(*tcm);
110 preimage.push(index);
111 // Hash the output to a field element.
112 let output_hash = N::hash_psd8(&preimage)?;
113
114 // Return the output ID.
115 Ok(OutputID::Public(output_hash))
116 }
117 // For a private output, compute the ciphertext (using `tvk`) and hash the ciphertext.
118 ValueType::Private(..) => {
119 // Ensure the output is a plaintext.
120 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
121 // Construct the (console) output index as a field element.
122 let index = Field::from_u16(
123 u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
124 );
125 // Compute the output view key as `Hash(function ID || tvk || index)`.
126 let output_view_key = N::hash_psd4(&[function_id, *tvk, index])?;
127 // Compute the ciphertext.
128 let ciphertext = match &output {
129 Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key)?,
130 // Ensure the output is a plaintext.
131 Value::Record(..) => bail!("Expected a plaintext output, found a record output"),
132 Value::Future(..) => bail!("Expected a plaintext output, found a future output"),
133 };
134 // Hash the ciphertext to a field element.
135 let output_hash = N::hash_psd8(&ciphertext.to_fields()?)?;
136 // Return the output ID.
137 Ok(OutputID::Private(output_hash))
138 }
139 // For a record output, compute the record commitment, and encrypt the record (using `tvk`).
140 ValueType::Record(record_name) => {
141 // Retrieve the record.
142 let record = match &output {
143 Value::Record(record) => record,
144 // Ensure the input is a record.
145 Value::Plaintext(..) => bail!("Expected a record output, found a plaintext output"),
146 Value::Future(..) => bail!("Expected a record output, found a future output"),
147 };
148
149 // Retrieve the output register.
150 let output_register = match output_register {
151 Some(output_register) => output_register,
152 None => bail!("Expected a register to be paired with a record output"),
153 };
154
155 // Compute the record commitment.
156 let commitment = record.to_commitment(program_id, record_name)?;
157
158 // Construct the (console) output index as a field element.
159 let index = Field::from_u64(output_register.locator());
160 // Compute the encryption randomizer as `HashToScalar(tvk || index)`.
161 let randomizer = N::hash_to_scalar_psd2(&[*tvk, index])?;
162
163 // Encrypt the record, using the randomizer.
164 let encrypted_record = record.encrypt(randomizer)?;
165 // Compute the record checksum, as the hash of the encrypted record.
166 let checksum = N::hash_bhp1024(&encrypted_record.to_bits_le())?;
167
168 // Return the output ID.
169 Ok(OutputID::Record(commitment, checksum))
170 }
171 // For a locator output, compute the hash (using `tvk`) of the output.
172 ValueType::ExternalRecord(..) => {
173 // Ensure the output is a record.
174 ensure!(matches!(output, Value::Record(..)), "Expected a record output");
175
176 // Construct the (console) output index as a field element.
177 let index = Field::from_u16(
178 u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
179 );
180 // Construct the preimage as `(function ID || output || tvk || index)`.
181 let mut preimage = Vec::new();
182 preimage.push(function_id);
183 preimage.extend(output.to_fields()?);
184 preimage.push(*tvk);
185 preimage.push(index);
186 // Hash the output to a field element.
187 let output_hash = N::hash_psd8(&preimage)?;
188
189 // Return the output ID.
190 Ok(OutputID::ExternalRecord(output_hash))
191 }
192 // For a future output, compute the hash (using `tcm`) of the output.
193 ValueType::Future(..) => {
194 // Ensure the output is a future.
195 ensure!(matches!(output, Value::Future(..)), "Expected a future output");
196
197 // Construct the (console) output index as a field element.
198 let index = Field::from_u16(
199 u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
200 );
201 // Construct the preimage as `(function ID || output || tcm || index)`.
202 let mut preimage = Vec::new();
203 preimage.push(function_id);
204 preimage.extend(output.to_fields()?);
205 preimage.push(*tcm);
206 preimage.push(index);
207 // Hash the output to a field element.
208 let output_hash = N::hash_psd8(&preimage)?;
209
210 // Return the output ID.
211 Ok(OutputID::Future(output_hash))
212 }
213 }
214 })
215 .collect::<Result<Vec<_>>>()?;
216
217 Ok(Self { output_ids, outputs })
218 }
219
220 /// Returns the output ID for the transition.
221 pub fn output_ids(&self) -> &[OutputID<N>] {
222 &self.output_ids
223 }
224
225 /// Returns the function outputs.
226 pub fn outputs(&self) -> &[Value<N>] {
227 &self.outputs
228 }
229}