snarkvm_console_program/request/
mod.rs1mod input_id;
17pub use input_id::InputID;
18
19mod bytes;
20mod serialize;
21mod sign;
22mod string;
23mod verify;
24
25use crate::{DynamicRecord, Identifier, Plaintext, ProgramID, Record, Value, ValueType, compute_function_id};
26use snarkvm_console_account::{Address, ComputeKey, GraphKey, PrivateKey, Signature, ViewKey};
27use snarkvm_console_network::Network;
28use snarkvm_console_types::prelude::*;
29
30#[derive(Clone, PartialEq, Eq)]
31pub struct Request<N: Network> {
32 signer: Address<N>,
34 network_id: U16<N>,
36 program_id: ProgramID<N>,
38 function_name: Identifier<N>,
40 input_ids: Vec<InputID<N>>,
42 inputs: Vec<Value<N>>,
44 signature: Signature<N>,
46 sk_tag: Field<N>,
48 tvk: Field<N>,
50 tcm: Field<N>,
52 scm: Field<N>,
54 is_dynamic: bool,
56}
57
58impl<N: Network>
59 From<(
60 Address<N>,
61 U16<N>,
62 ProgramID<N>,
63 Identifier<N>,
64 Vec<InputID<N>>,
65 Vec<Value<N>>,
66 Signature<N>,
67 Field<N>,
68 Field<N>,
69 Field<N>,
70 Field<N>,
71 bool,
72 )> for Request<N>
73{
74 fn from(
76 (
77 signer,
78 network_id,
79 program_id,
80 function_name,
81 input_ids,
82 inputs,
83 signature,
84 sk_tag,
85 tvk,
86 tcm,
87 scm,
88 is_dynamic,
89 ): (
90 Address<N>,
91 U16<N>,
92 ProgramID<N>,
93 Identifier<N>,
94 Vec<InputID<N>>,
95 Vec<Value<N>>,
96 Signature<N>,
97 Field<N>,
98 Field<N>,
99 Field<N>,
100 Field<N>,
101 bool,
102 ),
103 ) -> Self {
104 if inputs.len() != input_ids.len() {
106 N::halt(format!(
107 "Invalid request: mismatching number of input IDs ({}) and inputs ({})",
108 input_ids.len(),
109 inputs.len()
110 ))
111 }
112
113 if *network_id != N::ID {
115 N::halt(format!("Invalid network ID. Expected {}, found {}", N::ID, *network_id))
116 } else {
117 Self {
118 signer,
119 network_id,
120 program_id,
121 function_name,
122 input_ids,
123 inputs,
124 signature,
125 sk_tag,
126 tvk,
127 tcm,
128 scm,
129 is_dynamic,
130 }
131 }
132 }
133}
134
135impl<N: Network> Request<N> {
136 pub const fn signer(&self) -> &Address<N> {
138 &self.signer
139 }
140
141 pub const fn network_id(&self) -> &U16<N> {
143 &self.network_id
144 }
145
146 pub const fn program_id(&self) -> &ProgramID<N> {
148 &self.program_id
149 }
150
151 pub const fn function_name(&self) -> &Identifier<N> {
153 &self.function_name
154 }
155
156 pub fn input_ids(&self) -> &[InputID<N>] {
158 &self.input_ids
159 }
160
161 pub fn inputs(&self) -> &[Value<N>] {
163 &self.inputs
164 }
165
166 pub const fn signature(&self) -> &Signature<N> {
168 &self.signature
169 }
170
171 pub const fn sk_tag(&self) -> &Field<N> {
173 &self.sk_tag
174 }
175
176 pub const fn tvk(&self) -> &Field<N> {
178 &self.tvk
179 }
180
181 pub fn to_tpk(&self) -> Group<N> {
183 let challenge = self.signature.challenge();
185 let response = self.signature.response();
187 let pk_sig = self.signature.compute_key().pk_sig();
189 (pk_sig * challenge) + N::g_scalar_multiply(&response)
191 }
192
193 pub const fn tcm(&self) -> &Field<N> {
195 &self.tcm
196 }
197
198 pub const fn scm(&self) -> &Field<N> {
200 &self.scm
201 }
202
203 pub const fn is_dynamic(&self) -> bool {
205 self.is_dynamic
206 }
207
208 pub fn to_dynamic_input_ids(&self) -> Result<Vec<InputID<N>>> {
215 let function_id = compute_function_id(&self.network_id, &self.program_id, &self.function_name)?;
217
218 ensure!(
219 self.input_ids().len() == self.inputs.len(),
220 "Mismatched number of input IDs and inputs: {} vs. {}",
221 self.input_ids().len(),
222 self.inputs.len(),
223 );
224
225 self.input_ids()
227 .iter()
228 .zip(self.inputs.iter())
229 .enumerate()
230 .map(|(index, (input_id, input))| match (input_id, input) {
231 (InputID::Constant(..), Value::Plaintext(..))
232 | (InputID::Public(..), Value::Plaintext(..))
233 | (InputID::Private(..), Value::Plaintext(..))
234 | (InputID::DynamicRecord(..), Value::DynamicRecord(..)) => Ok(*input_id),
235 (InputID::Record(..), Value::Record(record)) | (InputID::ExternalRecord(..), Value::Record(record)) => {
236 let index = u16::try_from(index).map_err(|_| anyhow!("Input index exceeds u16"))?;
238 let caller_input = Value::DynamicRecord(DynamicRecord::from_record(record)?);
240 InputID::dynamic_record(function_id, &caller_input, self.tvk, index)
242 }
243 _ => bail!("Mismatching input ID and input value at index {index}"),
244 })
245 .collect()
246 }
247
248 pub fn to_dynamic_inputs(&self) -> Result<Vec<Value<N>>> {
252 self.inputs
253 .iter()
254 .map(|input| match input {
255 Value::Record(record) => {
256 Ok(Value::DynamicRecord(DynamicRecord::from_record(record)?))
258 }
259 Value::Future(_) => bail!("A future cannot be an input to a request."),
260 Value::DynamicFuture(_) => bail!("A dynamic future cannot be an input to a request."),
261 _ => Ok(input.clone()),
262 })
263 .collect::<Result<Vec<_>>>()
264 }
265}
266
267#[cfg(test)]
268mod test_helpers {
269 use super::*;
270 use snarkvm_console_network::MainnetV0;
271
272 type CurrentNetwork = MainnetV0;
273
274 const ITERATIONS: u64 = 1000;
275
276 pub(super) fn sample_requests(rng: &mut TestRng) -> Vec<Request<CurrentNetwork>> {
277 (0..ITERATIONS)
278 .map(|i| {
279 let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
281 let address = Address::try_from(&private_key).unwrap();
282
283 let program_id = ProgramID::from_str("token.aleo").unwrap();
285 let function_name = Identifier::from_str("transfer").unwrap();
286
287 let record_string =
289 format!("{{ owner: {address}.private, token_amount: {i}u64.private, _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public }}");
290
291 let input_constant = Value::from_str(&format!("{{ token_amount: {i}u128 }}")).unwrap();
293 let input_public = Value::from_str(&format!("{{ token_amount: {i}u128 }}")).unwrap();
294 let input_private = Value::from_str(&format!("{{ token_amount: {i}u128 }}")).unwrap();
295 let input_record = Value::from_str(&record_string).unwrap();
296 let input_external_record = Value::from_str(&record_string).unwrap();
297 let inputs = vec![input_constant, input_public, input_private, input_record, input_external_record];
298
299 let input_types = [
301 ValueType::from_str("amount.constant").unwrap(),
302 ValueType::from_str("amount.public").unwrap(),
303 ValueType::from_str("amount.private").unwrap(),
304 ValueType::from_str("token.record").unwrap(),
305 ValueType::from_str("token.aleo/token.record").unwrap(),
306 ];
307
308 let root_tvk = None;
310 let is_root = Uniform::rand(rng);
312 let program_checksum = match bool::rand(rng) {
314 true => Some(Field::rand(rng)),
315 false => None,
316 };
317
318 let is_dynamic = bool::rand(rng);
320 let request = Request::sign(
322 &private_key,
323 program_id,
324 function_name,
325 inputs.into_iter(),
326 &input_types,
327 root_tvk,
328 is_root,
329 program_checksum,
330 is_dynamic,
331 rng,
332 )
333 .unwrap();
334 assert!(request.verify(&input_types, is_root, program_checksum));
335 request
336 })
337 .collect()
338 }
339}