/ circuit / program / src / response / process_outputs_from_callback.rs
process_outputs_from_callback.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm 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  use super::*;
 17  
 18  impl<A: Alpha> Response<A> {
 19      /// Returns the injected circuit outputs, given the number of inputs, tvk, tcm, outputs, output types, and output registers.
 20      pub fn process_outputs_from_callback(
 21          network_id: &U16<A>,
 22          program_id: &ProgramID<A>,
 23          function_name: &Identifier<A>,
 24          num_inputs: usize,
 25          tvk: &Field<A>,
 26          tcm: &Field<A>,
 27          outputs: Vec<console::Value<A::Network>>,        // Note: Console type
 28          output_types: &[console::ValueType<A::Network>], // Note: Console type
 29          output_registers: &[Option<console::Register<A::Network>>], // Note: Console type
 30      ) -> Vec<Value<A>> {
 31          // Compute the function ID.
 32          let function_id = compute_function_id(network_id, program_id, function_name);
 33  
 34          match outputs
 35              .iter()
 36              .zip_eq(output_types)
 37              .zip_eq(output_registers)
 38              .enumerate()
 39              .map(|(index, ((output, output_types), output_register))| {
 40                  match output_types {
 41                      // For a constant output, compute the hash (using `tcm`) of the output.
 42                      console::ValueType::Constant(..) => {
 43                          // Inject the output as `Mode::Constant`.
 44                          let output = Value::new(Mode::Constant, output.clone());
 45                          // Ensure the output is a plaintext.
 46                          ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
 47  
 48                          // Prepare the index as a constant field element.
 49                          let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
 50                          // Construct the preimage as `(function ID || output || tcm || index)`.
 51                          let mut preimage = Vec::new();
 52                          preimage.push(function_id.clone());
 53                          preimage.extend(output.to_fields());
 54                          preimage.push(tcm.clone());
 55                          preimage.push(output_index);
 56  
 57                          // Hash the output to a field element.
 58                          match &output {
 59                              // Return the output ID.
 60                              Value::Plaintext(..) => Ok((OutputID::constant(A::hash_psd8(&preimage)), output)),
 61                              // Ensure the output is a plaintext.
 62                              Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
 63                              Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
 64                          }
 65                      }
 66                      // For a public output, compute the hash (using `tcm`) of the output.
 67                      console::ValueType::Public(..) => {
 68                          // Inject the output as `Mode::Private`.
 69                          let output = Value::new(Mode::Private, output.clone());
 70                          // Ensure the output is a plaintext.
 71                          ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
 72  
 73                          // Prepare the index as a constant field element.
 74                          let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
 75                          // Construct the preimage as `(function ID || output || tcm || index)`.
 76                          let mut preimage = Vec::new();
 77                          preimage.push(function_id.clone());
 78                          preimage.extend(output.to_fields());
 79                          preimage.push(tcm.clone());
 80                          preimage.push(output_index);
 81  
 82                          // Hash the output to a field element.
 83                          match &output {
 84                              // Return the output ID.
 85                              Value::Plaintext(..) => Ok((OutputID::public(A::hash_psd8(&preimage)), output)),
 86                              // Ensure the output is a plaintext.
 87                              Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
 88                              Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
 89                          }
 90                      }
 91                      // For a private output, compute the ciphertext (using `tvk`) and hash the ciphertext.
 92                      console::ValueType::Private(..) => {
 93                          // Inject the output as `Mode::Private`.
 94                          let output = Value::new(Mode::Private, output.clone());
 95                          // Ensure the output is a plaintext.
 96                          ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
 97  
 98                          // Prepare the index as a constant field element.
 99                          let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
100                          // Compute the output view key as `Hash(function ID || tvk || index)`.
101                          let output_view_key = A::hash_psd4(&[function_id.clone(), tvk.clone(), output_index]);
102                          // Compute the ciphertext.
103                          let ciphertext = match &output {
104                              Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key),
105                              // Ensure the output is a plaintext.
106                              Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
107                              Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
108                          };
109                          // Return the output ID.
110                          Ok((OutputID::private(A::hash_psd8(&ciphertext.to_fields())), output))
111                      }
112                      // For a record output, compute the record commitment.
113                      console::ValueType::Record(record_name) => {
114                          // Inject the output as `Mode::Private`.
115                          let output = Value::new(Mode::Private, output.clone());
116  
117                          // Retrieve the record.
118                          let record = match &output {
119                              Value::Record(record) => record,
120                              // Ensure the output is a record.
121                              Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
122                              Value::Future(..) => A::halt("Expected a record output, found a future output"),
123                          };
124  
125                          // Retrieve the output register.
126                          let output_register = match output_register {
127                              Some(output_register) => output_register,
128                              None => A::halt("Expected a register to be paired with a record output"),
129                          };
130  
131                          // Prepare the index as a constant field element.
132                          let output_index = Field::constant(console::Field::from_u64(output_register.locator()));
133                          // Compute the encryption randomizer as `HashToScalar(tvk || index)`.
134                          let randomizer = A::hash_to_scalar_psd2(&[tvk.clone(), output_index]);
135  
136                          // Compute the record view key.
137                          let record_view_key = ((*record.owner()).to_group() * randomizer).to_x_coordinate();
138                          // Compute the record commitment.
139                          let commitment =
140                              record.to_commitment(program_id, &Identifier::constant(*record_name), &record_view_key);
141  
142                          // Return the output ID.
143                          // Note: Because this is a callback, the output ID is an **external record** ID.
144                          Ok((OutputID::external_record(commitment), output))
145                      }
146                      // For an external record output, compute the hash (using `tvk`) of the output.
147                      console::ValueType::ExternalRecord(..) => {
148                          // Inject the output as `Mode::Private`.
149                          let output = Value::new(Mode::Private, output.clone());
150                          // Ensure the output is a record.
151                          ensure!(matches!(output, Value::Record(..)), "Expected a record output");
152  
153                          // Prepare the index as a constant field element.
154                          let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
155                          // Construct the preimage as `(function ID || output || tvk || index)`.
156                          let mut preimage = Vec::new();
157                          preimage.push(function_id.clone());
158                          preimage.extend(output.to_fields());
159                          preimage.push(tvk.clone());
160                          preimage.push(output_index);
161  
162                          // Return the output ID.
163                          match &output {
164                              Value::Record(..) => Ok((OutputID::external_record(A::hash_psd8(&preimage)), output)),
165                              // Ensure the output is a record.
166                              Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
167                              Value::Future(..) => A::halt("Expected a record output, found a future output"),
168                          }
169                      }
170                      // For a future output, compute the hash (using `tcm`) of the output.
171                      console::ValueType::Future(..) => {
172                          // Inject the output as `Mode::Private`.
173                          let output = Value::new(Mode::Private, output.clone());
174                          // Ensure the output is a future.
175                          ensure!(matches!(output, Value::Future(..)), "Expected a future output");
176  
177                          // Prepare the index as a constant field element.
178                          let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
179                          // Construct the preimage as `(function ID || output || tcm || index)`.
180                          let mut preimage = Vec::new();
181                          preimage.push(function_id.clone());
182                          preimage.extend(output.to_fields());
183                          preimage.push(tcm.clone());
184                          preimage.push(output_index);
185  
186                          // Hash the output to a field element.
187                          match &output {
188                              // Return the output ID.
189                              Value::Future(..) => Ok((OutputID::future(A::hash_psd8(&preimage)), output)),
190                              // Ensure the output is a future.
191                              Value::Plaintext(..) => A::halt("Expected a future output, found a plaintext output"),
192                              Value::Record(..) => A::halt("Expected a future output, found a record output"),
193                          }
194                      }
195                  }
196              })
197              .collect::<Result<Vec<_>>>()
198          {
199              Ok(outputs) => {
200                  // Unzip the output IDs from the output values.
201                  let (_, outputs): (Vec<OutputID<A>>, _) = outputs.into_iter().unzip();
202                  // Return the outputs.
203                  outputs
204              }
205              Err(error) => A::halt(error.to_string()),
206          }
207      }
208  }
209  
210  #[cfg(test)]
211  mod tests {
212      use super::*;
213      use crate::Circuit;
214      use deltavm_utilities::{TestRng, Uniform};
215  
216      use anyhow::Result;
217  
218      pub(crate) const ITERATIONS: usize = 20;
219  
220      fn check_from_callback(
221          mode: Mode,
222          num_constants: u64,
223          num_public: u64,
224          num_private: u64,
225          num_constraints: u64,
226      ) -> Result<()> {
227          use console::Network;
228  
229          let rng = &mut TestRng::default();
230  
231          for i in 0..ITERATIONS {
232              // Sample a `tvk`.
233              let tvk = console::Field::rand(rng);
234              // Compute the transition commitment as `Hash(tvk)`.
235              let tcm = <Circuit as Environment>::Network::hash_psd2(&[tvk])?;
236  
237              // Compute the nonce.
238              let index = console::Field::from_u64(8);
239              let randomizer = <Circuit as Environment>::Network::hash_to_scalar_psd2(&[tvk, index]).unwrap();
240              let nonce = <Circuit as Environment>::Network::g_scalar_multiply(&randomizer);
241  
242              // Construct the outputs.
243              let output_constant = console::Value::<<Circuit as Environment>::Network>::Plaintext(
244                  console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
245              );
246              let output_public = console::Value::<<Circuit as Environment>::Network>::Plaintext(
247                  console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
248              );
249              let output_private = console::Value::<<Circuit as Environment>::Network>::Plaintext(
250                  console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
251              );
252              let output_record = console::Value::<<Circuit as Environment>::Network>::Record(console::Record::from_str(&format!("{{ owner: dx150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, token_amount: 100u64.private, _nonce: {nonce}.public }}")).unwrap());
253              let output_external_record = console::Value::<<Circuit as Environment>::Network>::Record(console::Record::from_str("{ owner: dx150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, token_amount: 100u64.private, _nonce: 0group.public }").unwrap());
254              let outputs = vec![output_constant, output_public, output_private, output_record, output_external_record];
255  
256              // Construct the output types.
257              let output_types = vec![
258                  console::ValueType::from_str("amount.constant").unwrap(),
259                  console::ValueType::from_str("amount.public").unwrap(),
260                  console::ValueType::from_str("amount.private").unwrap(),
261                  console::ValueType::from_str("token.record").unwrap(),
262                  console::ValueType::from_str("token.delta/token.record").unwrap(),
263              ];
264  
265              // Construct the output registers.
266              let output_registers = vec![
267                  Some(console::Register::Locator(5)),
268                  Some(console::Register::Locator(6)),
269                  Some(console::Register::Locator(7)),
270                  Some(console::Register::Locator(8)),
271                  Some(console::Register::Locator(9)),
272              ];
273  
274              // Construct a signer.
275              let signer = console::Address::rand(rng);
276              // Construct a network ID.
277              let network_id = console::U16::new(<Circuit as Environment>::Network::ID);
278              // Construct a program ID.
279              let program_id = console::ProgramID::from_str("test.delta")?;
280              // Construct a function name.
281              let function_name = console::Identifier::from_str("check")?;
282  
283              // Construct the response.
284              let response = console::Response::new(
285                  &signer,
286                  &network_id,
287                  &program_id,
288                  &function_name,
289                  4,
290                  &tvk,
291                  &tcm,
292                  outputs.clone(),
293                  &output_types,
294                  &output_registers,
295              )?;
296              // assert!(response.verify());
297  
298              // Inject the signer, network ID, program ID, function name, `tvk`, `tcm`.
299              let signer = Address::<Circuit>::new(mode, signer);
300              let network_id = U16::<Circuit>::constant(network_id);
301              let program_id = ProgramID::<Circuit>::new(mode, program_id);
302              let function_name = Identifier::<Circuit>::new(mode, function_name);
303              let tvk = Field::<Circuit>::new(mode, tvk);
304              let tcm = Field::<Circuit>::new(mode, tcm);
305  
306              Circuit::scope(format!("Response {i}"), || {
307                  let outputs = Response::process_outputs_from_callback(
308                      &network_id,
309                      &program_id,
310                      &function_name,
311                      4,
312                      &tvk,
313                      &tcm,
314                      response.outputs().to_vec(),
315                      &output_types,
316                      &output_registers,
317                  );
318                  assert_eq!(response.outputs(), outputs.eject_value());
319                  match mode.is_constant() {
320                      true => assert_scope!(<=num_constants, <=num_public, <=num_private, <=num_constraints),
321                      false => assert_scope!(<=num_constants, num_public, num_private, num_constraints),
322                  }
323              });
324  
325              // Compute the response using outputs (circuit).
326              let outputs = Inject::new(mode, response.outputs().to_vec());
327              let candidate_b = Response::from_outputs(
328                  &signer,
329                  &network_id,
330                  &program_id,
331                  &function_name,
332                  4,
333                  &tvk,
334                  &tcm,
335                  outputs,
336                  &output_types,
337                  &output_registers,
338              );
339              assert_eq!(response, candidate_b.eject_value());
340  
341              Circuit::reset();
342          }
343          Ok(())
344      }
345  
346      // Note: These counts are correct. At this (high) level of a program, we override the default mode in many cases,
347      // based on the user-defined visibility in the types. Thus, we have nonzero public, private, and constraint values.
348      // These bounds are determined experimentally.
349  
350      #[test]
351      fn test_from_callback_constant() -> Result<()> {
352          check_from_callback(Mode::Constant, 35000, 5, 11500, 11500)
353      }
354  
355      #[test]
356      fn test_from_callback_public() -> Result<()> {
357          check_from_callback(Mode::Public, 34374, 5, 13475, 13490)
358      }
359  
360      #[test]
361      fn test_from_callback_private() -> Result<()> {
362          check_from_callback(Mode::Private, 34374, 5, 13475, 13490)
363      }
364  }