snarkvm_circuit_program/response/
from_outputs.rs1use super::*;
17
18impl<A: Aleo> Response<A> {
19 pub fn from_outputs(
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<Value<A>>,
28 output_types: &[console::ValueType<A::Network>], output_registers: &[Option<console::Register<A::Network>>], ) -> Self {
31 let function_id = compute_function_id(network_id, program_id, function_name);
33
34 let output_ids = outputs
36 .iter()
37 .zip_eq(output_types)
38 .zip_eq(output_registers)
39 .enumerate()
40 .map(|(index, ((output, output_type), output_register))| {
41 match output_type {
42 console::ValueType::Constant(..) => {
44 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
46 let mut preimage = Vec::new();
48 preimage.push(function_id.clone());
49 preimage.extend(output.to_fields());
50 preimage.push(tcm.clone());
51 preimage.push(output_index);
52
53 match &output {
55 Value::Plaintext(..) => OutputID::constant(A::hash_psd8(&preimage)),
57 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
59 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
60 }
61 }
62 console::ValueType::Public(..) => {
64 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
66 let mut preimage = Vec::new();
68 preimage.push(function_id.clone());
69 preimage.extend(output.to_fields());
70 preimage.push(tcm.clone());
71 preimage.push(output_index);
72
73 match &output {
75 Value::Plaintext(..) => OutputID::public(A::hash_psd8(&preimage)),
77 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
79 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
80 }
81 }
82 console::ValueType::Private(..) => {
84 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
86 let output_view_key = A::hash_psd4(&[function_id.clone(), tvk.clone(), output_index]);
88 let ciphertext = match &output {
90 Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key),
91 Value::Record(..) => A::halt("Expected a plaintext output, found a record output"),
93 Value::Future(..) => A::halt("Expected a plaintext output, found a future output"),
94 };
95 OutputID::private(A::hash_psd8(&ciphertext.to_fields()))
97 }
98 console::ValueType::Record(record_name) => {
100 let record = match &output {
102 Value::Record(record) => record,
103 Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
105 Value::Future(..) => A::halt("Expected a record output, found a future output"),
106 };
107
108 let output_register = match output_register {
110 Some(output_register) => output_register,
111 None => A::halt("Expected a register to be paired with a record output"),
112 };
113
114 let commitment = record.to_commitment(program_id, &Identifier::constant(*record_name));
116
117 let output_index = Field::constant(console::Field::from_u64(output_register.locator()));
119 let randomizer = A::hash_to_scalar_psd2(&[tvk.clone(), output_index]);
121
122 let encrypted_record = record.encrypt(&randomizer);
124 let checksum = A::hash_bhp1024(&encrypted_record.to_bits_le());
126
127 OutputID::record(commitment, checksum)
129 }
130 console::ValueType::ExternalRecord(..) => {
132 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
134 let mut preimage = Vec::new();
136 preimage.push(function_id.clone());
137 preimage.extend(output.to_fields());
138 preimage.push(tvk.clone());
139 preimage.push(output_index);
140
141 match &output {
143 Value::Record(..) => OutputID::external_record(A::hash_psd8(&preimage)),
144 Value::Plaintext(..) => A::halt("Expected a record output, found a plaintext output"),
146 Value::Future(..) => A::halt("Expected a record output, found a future output"),
147 }
148 }
149 console::ValueType::Future(..) => {
151 let output_index = Field::constant(console::Field::from_u16((num_inputs + index) as u16));
153 let mut preimage = Vec::new();
155 preimage.push(function_id.clone());
156 preimage.extend(output.to_fields());
157 preimage.push(tcm.clone());
158 preimage.push(output_index);
159
160 match &output {
162 Value::Future(..) => OutputID::future(A::hash_psd8(&preimage)),
164 Value::Plaintext(..) => A::halt("Expected a future output, found a plaintext output"),
166 Value::Record(..) => A::halt("Expected a future output, found a record output"),
167 }
168 }
169 }
170 })
171 .collect();
172
173 Self { output_ids, outputs }
175 }
176}
177
178#[cfg(all(test, feature = "console"))]
179mod tests {
180 use super::*;
181 use crate::Circuit;
182 use snarkvm_utilities::{TestRng, Uniform};
183
184 use anyhow::Result;
185 use snarkvm_circuit_types::U16;
186
187 pub(crate) const ITERATIONS: usize = 20;
188
189 fn check_from_outputs(
190 mode: Mode,
191 num_constants: u64,
192 num_public: u64,
193 num_private: u64,
194 num_constraints: u64,
195 ) -> Result<()> {
196 use console::Network;
197
198 let rng = &mut TestRng::default();
199
200 for i in 0..ITERATIONS {
201 let tvk = console::Field::rand(rng);
203 let tcm = <Circuit as Environment>::Network::hash_psd2(&[tvk])?;
205
206 let index = console::Field::from_u64(8);
208 let randomizer = <Circuit as Environment>::Network::hash_to_scalar_psd2(&[tvk, index]).unwrap();
209 let nonce = <Circuit as Environment>::Network::g_scalar_multiply(&randomizer);
210
211 let output_constant = console::Value::<<Circuit as Environment>::Network>::Plaintext(
213 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
214 );
215 let output_public = console::Value::<<Circuit as Environment>::Network>::Plaintext(
216 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
217 );
218 let output_private = console::Value::<<Circuit as Environment>::Network>::Plaintext(
219 console::Plaintext::from_str("{ token_amount: 9876543210u128 }").unwrap(),
220 );
221 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());
222 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());
223 let outputs = vec![output_constant, output_public, output_private, output_record, output_external_record];
224
225 let output_types = vec![
227 console::ValueType::from_str("amount.constant").unwrap(),
228 console::ValueType::from_str("amount.public").unwrap(),
229 console::ValueType::from_str("amount.private").unwrap(),
230 console::ValueType::from_str("token.record").unwrap(),
231 console::ValueType::from_str("token.aleo/token.record").unwrap(),
232 ];
233
234 let output_registers = vec![
236 Some(console::Register::Locator(5)),
237 Some(console::Register::Locator(6)),
238 Some(console::Register::Locator(7)),
239 Some(console::Register::Locator(8)),
240 Some(console::Register::Locator(9)),
241 ];
242
243 let network_id = console::U16::new(<Circuit as Environment>::Network::ID);
245 let program_id = console::ProgramID::from_str("test.aleo")?;
247 let function_name = console::Identifier::from_str("check")?;
249
250 let response = console::Response::new(
252 &network_id,
253 &program_id,
254 &function_name,
255 4,
256 &tvk,
257 &tcm,
258 outputs.clone(),
259 &output_types,
260 &output_registers,
261 )?;
262
263 let network_id = U16::<Circuit>::constant(network_id);
265 let program_id = ProgramID::<Circuit>::new(mode, program_id);
266 let function_name = Identifier::<Circuit>::new(mode, function_name);
267 let tvk = Field::<Circuit>::new(mode, tvk);
268 let tcm = Field::<Circuit>::new(mode, tcm);
269 let outputs = Inject::new(mode, outputs);
270
271 Circuit::scope(format!("Response {i}"), || {
272 let candidate = Response::from_outputs(
274 &network_id,
275 &program_id,
276 &function_name,
277 4,
278 &tvk,
279 &tcm,
280 outputs,
281 &output_types,
282 &output_registers,
283 );
284 assert_eq!(response, candidate.eject_value());
285 match mode.is_constant() {
286 true => assert_scope!(<=num_constants, <=num_public, <=num_private, <=num_constraints),
287 false => assert_scope!(<=num_constants, num_public, num_private, num_constraints),
288 }
289 });
290 Circuit::reset();
291 }
292 Ok(())
293 }
294
295 #[test]
299 fn test_from_outputs_constant() -> Result<()> {
300 check_from_outputs(Mode::Constant, 26000, 6, 9500, 9500)
301 }
302
303 #[test]
304 fn test_from_outputs_public() -> Result<()> {
305 check_from_outputs(Mode::Public, 24849, 6, 13962, 13983)
306 }
307
308 #[test]
309 fn test_from_outputs_private() -> Result<()> {
310 check_from_outputs(Mode::Private, 24849, 6, 13962, 13983)
311 }
312}