1use 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 Constant(Field<N>),
24 Public(Field<N>),
26 Private(Field<N>),
28 Record(Field<N>, Field<N>, Field<N>),
30 ExternalRecord(Field<N>),
32 Future(Field<N>),
34 DynamicRecord(Field<N>),
36 DynamicFuture(Field<N>),
38}
39
40impl<N: Network> OutputID<N> {
41 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 pub fn constant(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
58 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
60 let index = Field::from_u16(index);
62 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 let hash = N::hash_psd8(&preimage)?;
70 Ok(Self::Constant(hash))
71 }
72
73 pub fn public(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
76 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
78 let index = Field::from_u16(index);
80 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 let hash = N::hash_psd8(&preimage)?;
88 Ok(Self::Public(hash))
89 }
90
91 pub fn private(function_id: Field<N>, output: &Value<N>, tvk: Field<N>, index: u16) -> Result<Self> {
94 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
96 let index = Field::from_u16(index);
98 let output_view_key = N::hash_psd4(&[function_id, tvk, index])?;
100 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 let hash = N::hash_psd8(&ciphertext.to_fields()?)?;
110 Ok(Self::Private(hash))
111 }
112
113 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 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 let index = Field::from_u64(output_register.locator());
133 let randomizer = N::hash_to_scalar_psd2(&[tvk, index])?;
135 let (encrypted_record, record_view_key) = record.encrypt_symmetric(randomizer)?;
137 let commitment = record.to_commitment(program_id, record_name, &record_view_key)?;
139 let checksum = N::hash_bhp1024(&encrypted_record.to_bits_le())?;
141 let randomizer = N::hash_psd4(&[N::encryption_domain(), record_view_key, Field::one()])?;
143 let sender_ciphertext = (**signer).to_x_coordinate() + randomizer;
145 Ok(Self::Record(commitment, checksum, sender_ciphertext))
146 }
147
148 pub fn external_record(function_id: Field<N>, output: &Value<N>, tvk: Field<N>, index: u16) -> Result<Self> {
151 ensure!(matches!(output, Value::Record(..)), "Expected a record output");
153 let index = Field::from_u16(index);
155 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 let hash = N::hash_psd8(&preimage)?;
163 Ok(Self::ExternalRecord(hash))
164 }
165
166 pub fn future(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
169 ensure!(matches!(output, Value::Future(..)), "Expected a future output");
171 let index = Field::from_u16(index);
173 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 let hash = N::hash_psd8(&preimage)?;
181 Ok(Self::Future(hash))
182 }
183
184 pub fn dynamic_record(function_id: Field<N>, output: &Value<N>, tvk: Field<N>, index: u16) -> Result<Self> {
187 ensure!(matches!(output, Value::DynamicRecord(..)), "Expected a dynamic record output");
189 let index = Field::from_u16(index);
191 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 let hash = N::hash_psd8(&preimage)?;
199 Ok(Self::DynamicRecord(hash))
200 }
201
202 pub fn dynamic_future(function_id: Field<N>, output: &Value<N>, tcm: Field<N>, index: u16) -> Result<Self> {
205 ensure!(matches!(output, Value::DynamicFuture(..)), "Expected a dynamic future output");
207 let index = Field::from_u16(index);
209 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 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 output_ids: Vec<OutputID<N>>,
225 outputs: Vec<Value<N>>,
227}
228
229impl<N: Network> From<(Vec<OutputID<N>>, Vec<Value<N>>)> for Response<N> {
230 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 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 let function_id = compute_function_id(network_id, program_id, function_name)?;
252
253 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 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 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 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 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 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 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 ValueType::DynamicRecord => {
300 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 ValueType::DynamicFuture => {
308 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 pub fn output_ids(&self) -> &[OutputID<N>] {
323 &self.output_ids
324 }
325
326 pub fn outputs(&self) -> &[Value<N>] {
328 &self.outputs
329 }
330
331 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 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 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 let function_id = compute_function_id(network_id, program_id, function_name)?;
366 let caller_outputs = self.to_dynamic_outputs()?;
368 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 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 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}