snarkvm_circuit_program/response/
process_outputs_from_callback.rs1use 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>], ) -> Vec<Value<A>> {
30 let function_id = compute_function_id(network_id, program_id, function_name);
32
33 match outputs
34 .iter()
35 .zip_eq(output_types)
36 .enumerate()
37 .map(|(index, (output, output_types))| {
38 match output_types {
39 console::ValueType::Constant(..) => {
41 let output = Value::new(Mode::Constant, output.clone());
43 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
45
46 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
48 let mut preimage = Vec::new();
50 preimage.push(function_id.clone());
51 preimage.extend(output.to_fields());
52 preimage.push(tcm.clone());
53 preimage.push(output_index);
54
55 match &output {
57 Value::Plaintext(..) => Ok((OutputID::constant(A::hash_psd8(&preimage)), output)),
59 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
61 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
62 }
63 }
64 console::ValueType::Public(..) => {
66 let output = Value::new(Mode::Private, output.clone());
68 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
70
71 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
73 let mut preimage = Vec::new();
75 preimage.push(function_id.clone());
76 preimage.extend(output.to_fields());
77 preimage.push(tcm.clone());
78 preimage.push(output_index);
79
80 match &output {
82 Value::Plaintext(..) => Ok((OutputID::public(A::hash_psd8(&preimage)), output)),
84 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
86 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
87 }
88 }
89 console::ValueType::Private(..) => {
91 let output = Value::new(Mode::Private, output.clone());
93 ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
95
96 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
98 let output_view_key = A::hash_psd4(&[function_id.clone(), tvk.clone(), output_index]);
100 let ciphertext = match &output {
102 Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key),
103 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
105 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
106 };
107 Ok((OutputID::private(A::hash_psd8(&ciphertext.to_fields())), output))
109 }
110 console::ValueType::Record(record_name) => {
112 let output = Value::new(Mode::Private, output.clone());
114
115 let record = match &output {
117 Value::Record(record) => record,
118 Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
120 Value::Future(..) => A::halt("Expected a record output, found a future output"),
121 };
122 let commitment = record.to_commitment(program_id, &Identifier::constant(*record_name));
124
125 Ok((OutputID::external_record(commitment), output))
128 }
129 console::ValueType::ExternalRecord(..) => {
131 let output = Value::new(Mode::Private, output.clone());
133 ensure!(matches!(output, Value::Record(..)), "Expected a record output");
135
136 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
138 let mut preimage = Vec::new();
140 preimage.push(function_id.clone());
141 preimage.extend(output.to_fields());
142 preimage.push(tvk.clone());
143 preimage.push(output_index);
144
145 match &output {
147 Value::Record(..) => Ok((OutputID::external_record(A::hash_psd8(&preimage)), output)),
148 Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
150 Value::Future(..) => A::halt("Expected a record output, found a future output"),
151 }
152 }
153 console::ValueType::Future(..) => {
155 let output = Value::new(Mode::Private, output.clone());
157 ensure!(matches!(output, Value::Future(..)), "Expected a future output");
159
160 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
162 let mut preimage = Vec::new();
164 preimage.push(function_id.clone());
165 preimage.extend(output.to_fields());
166 preimage.push(tcm.clone());
167 preimage.push(output_index);
168
169 match &output {
171 Value::Future(..) => Ok((OutputID::future(A::hash_psd8(&preimage)), output)),
173 Value::Plaintext(..) => A::halt("Expected a future output, found a plaintext output"),
175 Value::Record(..) => A::halt("Expected a future output, found a record output"),
176 }
177 }
178 }
179 })
180 .collect::<Result<Vec<_>>>()
181 {
182 Ok(outputs) => {
183 let (_, outputs): (Vec<OutputID<A>>, _) = outputs.into_iter().unzip();
185 outputs
187 }
188 Err(error) => A::halt(error.to_string()),
189 }
190 }
191}
192
193#[cfg(all(test, feature = "console"))]
194mod tests {
195 use super::*;
196 use crate::Circuit;
197 use snarkvm_utilities::{TestRng, Uniform};
198
199 use anyhow::Result;
200
201 pub(crate) const ITERATIONS: usize = 20;
202
203 fn check_from_callback(
204 mode: Mode,
205 num_constants: u64,
206 num_public: u64,
207 num_private: u64,
208 num_constraints: u64,
209 ) -> Result<()> {
210 use console::Network;
211
212 let rng = &mut TestRng::default();
213
214 for i in 0..ITERATIONS {
215 let tvk = console::Field::rand(rng);
217 let tcm = <Circuit as Environment>::Network::hash_psd2(&[tvk])?;
219
220 let index = console::Field::from_u64(8);
222 let randomizer = <Circuit as Environment>::Network::hash_to_scalar_psd2(&[tvk, index]).unwrap();
223 let nonce = <Circuit as Environment>::Network::g_scalar_multiply(&randomizer);
224
225 let output_constant = console::Value::<<Circuit as Environment>::Network>::Plaintext(
227 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
228 );
229 let output_public = console::Value::<<Circuit as Environment>::Network>::Plaintext(
230 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
231 );
232 let output_private = console::Value::<<Circuit as Environment>::Network>::Plaintext(
233 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
234 );
235 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());
236 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());
237 let outputs = vec![output_constant, output_public, output_private, output_record, output_external_record];
238
239 let output_types = vec![
241 console::ValueType::from_str("amount.constant").unwrap(),
242 console::ValueType::from_str("amount.public").unwrap(),
243 console::ValueType::from_str("amount.private").unwrap(),
244 console::ValueType::from_str("token.record").unwrap(),
245 console::ValueType::from_str("token.aleo/token.record").unwrap(),
246 ];
247
248 let output_registers = vec![
250 Some(console::Register::Locator(5)),
251 Some(console::Register::Locator(6)),
252 Some(console::Register::Locator(7)),
253 Some(console::Register::Locator(8)),
254 Some(console::Register::Locator(9)),
255 ];
256
257 let network_id = console::U16::new(<Circuit as Environment>::Network::ID);
259 let program_id = console::ProgramID::from_str("test.aleo")?;
261 let function_name = console::Identifier::from_str("check")?;
263
264 let response = console::Response::new(
266 &network_id,
267 &program_id,
268 &function_name,
269 4,
270 &tvk,
271 &tcm,
272 outputs.clone(),
273 &output_types,
274 &output_registers,
275 )?;
276 let network_id = U16::<Circuit>::constant(network_id);
280 let program_id = ProgramID::<Circuit>::new(mode, program_id);
281 let function_name = Identifier::<Circuit>::new(mode, function_name);
282 let tvk = Field::<Circuit>::new(mode, tvk);
283 let tcm = Field::<Circuit>::new(mode, tcm);
284
285 Circuit::scope(format!("Response {i}"), || {
286 let outputs = Response::process_outputs_from_callback(
287 &network_id,
288 &program_id,
289 &function_name,
290 4,
291 &tvk,
292 &tcm,
293 response.outputs().to_vec(),
294 &output_types,
295 );
296 assert_eq!(response.outputs(), outputs.eject_value());
297 match mode.is_constant() {
298 true => assert_scope!(<=num_constants, num_public, num_private, num_constraints),
299 false => assert_scope!(<=num_constants, num_public, num_private, num_constraints),
300 }
301 });
302
303 let outputs = Inject::new(mode, response.outputs().to_vec());
305 let candidate_b = Response::from_outputs(
306 &network_id,
307 &program_id,
308 &function_name,
309 4,
310 &tvk,
311 &tcm,
312 outputs,
313 &output_types,
314 &output_registers,
315 );
316 assert_eq!(response, candidate_b.eject_value());
317
318 Circuit::reset();
319 }
320 Ok(())
321 }
322
323 #[test]
328 fn test_from_callback_constant() -> Result<()> {
329 check_from_callback(Mode::Constant, 20844, 5, 4922, 4931)
330 }
331
332 #[test]
333 fn test_from_callback_public() -> Result<()> {
334 check_from_callback(Mode::Public, 20844, 5, 6217, 6226)
335 }
336
337 #[test]
338 fn test_from_callback_private() -> Result<()> {
339 check_from_callback(Mode::Private, 20844, 5, 6217, 6226)
340 }
341}