snarkvm_circuit_program/response/
from_outputs.rs1use super::*;
17
18impl<A: Aleo> Response<A> {
19 pub fn from_outputs(
21 signer: &Address<A>,
22 network_id: &U16<A>,
23 program_id: &ProgramID<A>,
24 function_name: &Identifier<A>,
25 num_inputs: usize,
26 tvk: &Field<A>,
27 tcm: &Field<A>,
28 outputs: Vec<Value<A>>,
29 output_types: &[console::ValueType<A::Network>], output_registers: &[Option<console::Register<A::Network>>], ) -> Self {
32 let function_id = compute_function_id(network_id, program_id, function_name);
34
35 let output_ids = outputs
37 .iter()
38 .zip_eq(output_types)
39 .zip_eq(output_registers)
40 .enumerate()
41 .map(|(index, ((output, output_type), output_register))| {
42 match output_type {
43 console::ValueType::Constant(..) => {
45 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
47 let mut preimage = Vec::new();
49 preimage.push(function_id.clone());
50 preimage.extend(output.to_fields());
51 preimage.push(tcm.clone());
52 preimage.push(output_index);
53
54 match &output {
56 Value::Plaintext(..) => OutputID::constant(A::hash_psd8(&preimage)),
58 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
60 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
61 }
62 }
63 console::ValueType::Public(..) => {
65 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
67 let mut preimage = Vec::new();
69 preimage.push(function_id.clone());
70 preimage.extend(output.to_fields());
71 preimage.push(tcm.clone());
72 preimage.push(output_index);
73
74 match &output {
76 Value::Plaintext(..) => OutputID::public(A::hash_psd8(&preimage)),
78 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
80 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
81 }
82 }
83 console::ValueType::Private(..) => {
85 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
87 let output_view_key = A::hash_psd4(&[function_id.clone(), tvk.clone(), output_index]);
89 let ciphertext = match &output {
91 Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key),
92 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
94 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
95 };
96 OutputID::private(A::hash_psd8(&ciphertext.to_fields()))
98 }
99 console::ValueType::Record(record_name) => {
101 let record = match &output {
103 Value::Record(record) => record,
104 Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
106 Value::Future(..) => A::halt("Expected a record output, found a future output"),
107 };
108
109 let output_register = match output_register {
111 Some(output_register) => output_register,
112 None => A::halt("Expected a register to be paired with a record output"),
113 };
114
115 let output_index = Field::constant(console::Field::from_u64(output_register.locator()));
117 let randomizer = A::hash_to_scalar_psd2(&[tvk.clone(), output_index]);
119
120 let (encrypted_record, record_view_key) = record.encrypt_symmetric(&randomizer);
122
123 let commitment =
125 record.to_commitment(program_id, &Identifier::constant(*record_name), &record_view_key);
126
127 let checksum = A::hash_bhp1024(&encrypted_record.to_bits_le());
129
130 let randomizer = A::hash_psd4(&[A::encryption_domain(), record_view_key, Field::one()]);
132 let sender_ciphertext = signer.to_group().to_x_coordinate() + randomizer;
134
135 OutputID::record(commitment, checksum, sender_ciphertext)
137 }
138 console::ValueType::ExternalRecord(..) => {
140 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
142 let mut preimage = Vec::new();
144 preimage.push(function_id.clone());
145 preimage.extend(output.to_fields());
146 preimage.push(tvk.clone());
147 preimage.push(output_index);
148
149 match &output {
151 Value::Record(..) => OutputID::external_record(A::hash_psd8(&preimage)),
152 Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
154 Value::Future(..) => A::halt("Expected a record output, found a future output"),
155 }
156 }
157 console::ValueType::Future(..) => {
159 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
161 let mut preimage = Vec::new();
163 preimage.push(function_id.clone());
164 preimage.extend(output.to_fields());
165 preimage.push(tcm.clone());
166 preimage.push(output_index);
167
168 match &output {
170 Value::Future(..) => OutputID::future(A::hash_psd8(&preimage)),
172 Value::Plaintext(..) => A::halt("Expected a future output, found a plaintext output"),
174 Value::Record(..) => A::halt("Expected a future output, found a record output"),
175 }
176 }
177 }
178 })
179 .collect();
180
181 Self { output_ids, outputs }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::Circuit;
190 use snarkvm_circuit_types::U16;
191 use snarkvm_utilities::{TestRng, Uniform};
192
193 use anyhow::Result;
194
195 pub(crate) const ITERATIONS: usize = 20;
196
197 fn check_from_outputs(
198 mode: Mode,
199 num_constants: u64,
200 num_public: u64,
201 num_private: u64,
202 num_constraints: u64,
203 ) -> Result<()> {
204 use console::Network;
205
206 let rng = &mut TestRng::default();
207
208 for i in 0..ITERATIONS {
209 let tvk = console::Field::rand(rng);
211 let tcm = <Circuit as Environment>::Network::hash_psd2(&[tvk])?;
213
214 let index = console::Field::from_u64(8);
216 let randomizer = <Circuit as Environment>::Network::hash_to_scalar_psd2(&[tvk, index]).unwrap();
217 let nonce = <Circuit as Environment>::Network::g_scalar_multiply(&randomizer);
218
219 let output_constant = console::Value::<<Circuit as Environment>::Network>::Plaintext(
221 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
222 );
223 let output_public = console::Value::<<Circuit as Environment>::Network>::Plaintext(
224 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
225 );
226 let output_private = console::Value::<<Circuit as Environment>::Network>::Plaintext(
227 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
228 );
229 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());
230 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());
231 let outputs = vec![output_constant, output_public, output_private, output_record, output_external_record];
232
233 let output_types = vec![
235 console::ValueType::from_str("amount.constant").unwrap(),
236 console::ValueType::from_str("amount.public").unwrap(),
237 console::ValueType::from_str("amount.private").unwrap(),
238 console::ValueType::from_str("token.record").unwrap(),
239 console::ValueType::from_str("token.aleo/token.record").unwrap(),
240 ];
241
242 let output_registers = vec![
244 Some(console::Register::Locator(5)),
245 Some(console::Register::Locator(6)),
246 Some(console::Register::Locator(7)),
247 Some(console::Register::Locator(8)),
248 Some(console::Register::Locator(9)),
249 ];
250
251 let signer = console::Address::rand(rng);
253 let network_id = console::U16::new(<Circuit as Environment>::Network::ID);
255 let program_id = console::ProgramID::from_str("test.aleo")?;
257 let function_name = console::Identifier::from_str("check")?;
259
260 let response = console::Response::new(
262 &signer,
263 &network_id,
264 &program_id,
265 &function_name,
266 4,
267 &tvk,
268 &tcm,
269 outputs.clone(),
270 &output_types,
271 &output_registers,
272 )?;
273
274 let signer = Address::<Circuit>::new(mode, signer);
276 let network_id = U16::<Circuit>::constant(network_id);
277 let program_id = ProgramID::<Circuit>::new(mode, program_id);
278 let function_name = Identifier::<Circuit>::new(mode, function_name);
279 let tvk = Field::<Circuit>::new(mode, tvk);
280 let tcm = Field::<Circuit>::new(mode, tcm);
281 let outputs = Inject::new(mode, outputs);
282
283 Circuit::scope(format!("Response {i}"), || {
284 let candidate = Response::from_outputs(
286 &signer,
287 &network_id,
288 &program_id,
289 &function_name,
290 4,
291 &tvk,
292 &tcm,
293 outputs,
294 &output_types,
295 &output_registers,
296 );
297 assert_eq!(response, candidate.eject_value());
298 match mode.is_constant() {
299 true => assert_scope!(<=num_constants, <=num_public, <=num_private, <=num_constraints),
300 false => assert_scope!(<=num_constants, num_public, num_private, num_constraints),
301 }
302 });
303 Circuit::reset();
304 }
305 Ok(())
306 }
307
308 #[test]
312 fn test_from_outputs_constant() -> Result<()> {
313 check_from_outputs(Mode::Constant, 38500, 7, 13500, 13500)
314 }
315
316 #[test]
317 fn test_from_outputs_public() -> Result<()> {
318 check_from_outputs(Mode::Public, 37257, 7, 18057, 18085)
319 }
320
321 #[test]
322 fn test_from_outputs_private() -> Result<()> {
323 check_from_outputs(Mode::Private, 37257, 7, 18057, 18085)
324 }
325}