1use super::*;
17
18impl<A: Aleo> Response<A> {
19 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>>, output_types: &[console::ValueType<A::Network>], output_registers: &[Option<console::Register<A::Network>>], ) -> Vec<Value<A>> {
31 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 console::ValueType::Constant(..) => {
43 let output = Value::new(Mode::Constant, output.clone());
45 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
47
48 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
50 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 match &output {
59 Value::Plaintext(..) => Ok((OutputID::constant(A::hash_psd8(&preimage)), output)),
61 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 console::ValueType::Public(..) => {
68 let output = Value::new(Mode::Private, output.clone());
70 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
72
73 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
75 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 match &output {
84 Value::Plaintext(..) => Ok((OutputID::public(A::hash_psd8(&preimage)), output)),
86 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 console::ValueType::Private(..) => {
93 let output = Value::new(Mode::Private, output.clone());
95 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
97
98 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
100 let output_view_key = A::hash_psd4(&[function_id.clone(), tvk.clone(), output_index]);
102 let ciphertext = match &output {
104 Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key),
105 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 Ok((OutputID::private(A::hash_psd8(&ciphertext.to_fields())), output))
111 }
112 console::ValueType::Record(record_name) => {
114 let output = Value::new(Mode::Private, output.clone());
116
117 let record = match &output {
119 Value::Record(record) => record,
120 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 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 let output_index = Field::constant(console::Field::from_u64(output_register.locator()));
133 let randomizer = A::hash_to_scalar_psd2(&[tvk.clone(), output_index]);
135
136 let record_view_key = ((*record.owner()).to_group() * randomizer).to_x_coordinate();
138 let commitment =
140 record.to_commitment(program_id, &Identifier::constant(*record_name), &record_view_key);
141
142 Ok((OutputID::external_record(commitment), output))
145 }
146 console::ValueType::ExternalRecord(..) => {
148 let output = Value::new(Mode::Private, output.clone());
150 ensure!(matches!(output, Value::Record(..)), "Expected a record output");
152
153 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
155 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 match &output {
164 Value::Record(..) => Ok((OutputID::external_record(A::hash_psd8(&preimage)), output)),
165 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 console::ValueType::Future(..) => {
172 let output = Value::new(Mode::Private, output.clone());
174 ensure!(matches!(output, Value::Future(..)), "Expected a future output");
176
177 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
179 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 match &output {
188 Value::Future(..) => Ok((OutputID::future(A::hash_psd8(&preimage)), output)),
190 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 let (_, outputs): (Vec<OutputID<A>>, _) = outputs.into_iter().unzip();
202 outputs
204 }
205 Err(error) => A::halt(error.to_string()),
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::Circuit;
214 use snarkvm_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 let tvk = console::Field::rand(rng);
234 let tcm = <Circuit as Environment>::Network::hash_psd2(&[tvk])?;
236
237 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 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: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.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: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.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 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.aleo/token.record").unwrap(),
263 ];
264
265 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 let signer = console::Address::rand(rng);
276 let network_id = console::U16::new(<Circuit as Environment>::Network::ID);
278 let program_id = console::ProgramID::from_str("test.aleo")?;
280 let function_name = console::Identifier::from_str("check")?;
282
283 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 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 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 #[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}