1#![allow(clippy::too_many_arguments)]
16
17use super::*;
18
19impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
20 pub fn execute<R: Rng + CryptoRng>(
27 &self,
28 private_key: &PrivateKey<N>,
29 (program_id, function_name): (impl TryInto<ProgramID<N>>, impl TryInto<Identifier<N>>),
30 inputs: impl ExactSizeIterator<Item = impl TryInto<Value<N>>>,
31 fee_record: Option<Record<N, Plaintext<N>>>,
32 priority_fee_in_microcredits: u64,
33 query: Option<Query<N, C::BlockStorage>>,
34 rng: &mut R,
35 ) -> Result<Transaction<N>> {
36 let authorization = self.authorize(private_key, program_id, function_name, inputs, rng)?;
38 let is_fee_required = !authorization.is_split();
40 let is_priority_fee_declared = priority_fee_in_microcredits > 0;
42 let execution = self.execute_authorization_raw(authorization, query.clone(), rng)?;
44 let fee = match is_fee_required || is_priority_fee_declared {
46 true => {
47 let (minimum_execution_cost, (_, _)) = execution_cost(self, &execution)?;
49 let execution_id = execution.to_execution_id()?;
51 let authorization = match fee_record {
53 Some(record) => self.authorize_fee_private(
54 private_key,
55 record,
56 minimum_execution_cost,
57 priority_fee_in_microcredits,
58 execution_id,
59 rng,
60 )?,
61 None => self.authorize_fee_public(
62 private_key,
63 minimum_execution_cost,
64 priority_fee_in_microcredits,
65 execution_id,
66 rng,
67 )?,
68 };
69 Some(self.execute_fee_authorization_raw(authorization, query, rng)?)
71 }
72 false => None,
73 };
74 Transaction::from_execution(execution, fee)
76 }
77
78 pub fn execute_authorization<R: Rng + CryptoRng>(
80 &self,
81 execute_authorization: Authorization<N>,
82 fee_authorization: Option<Authorization<N>>,
83 query: Option<Query<N, C::BlockStorage>>,
84 rng: &mut R,
85 ) -> Result<Transaction<N>> {
86 let execution = self.execute_authorization_raw(execute_authorization, query.clone(), rng)?;
88 let fee = match fee_authorization {
90 Some(authorization) => Some(self.execute_fee_authorization_raw(authorization, query, rng)?),
91 None => None,
92 };
93 Transaction::from_execution(execution, fee)
95 }
96
97 pub fn execute_fee_authorization<R: Rng + CryptoRng>(
99 &self,
100 authorization: Authorization<N>,
101 query: Option<Query<N, C::BlockStorage>>,
102 rng: &mut R,
103 ) -> Result<Fee<N>> {
104 debug_assert!(authorization.is_fee_private() || authorization.is_fee_public(), "Expected a fee authorization");
105 self.execute_fee_authorization_raw(authorization, query, rng)
106 }
107}
108
109impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
110 #[inline]
113 fn execute_authorization_raw<R: Rng + CryptoRng>(
114 &self,
115 authorization: Authorization<N>,
116 query: Option<Query<N, C::BlockStorage>>,
117 rng: &mut R,
118 ) -> Result<Execution<N>> {
119 let timer = timer!("VM::execute_authorization_raw");
120
121 let locator = {
123 let request = authorization.peek_next()?;
124 Locator::new(*request.program_id(), *request.function_name()).to_string()
125 };
126 let query = match query {
128 Some(query) => query,
129 None => Query::VM(self.block_store().clone()),
130 };
131 lap!(timer, "Prepare the query");
132
133 macro_rules! logic {
134 ($process:expr, $network:path, $aleo:path) => {{
135 let authorization = cast_ref!(authorization as Authorization<$network>);
137 let (_, mut trace) = $process.execute::<$aleo, _>(authorization.clone(), rng)?;
139 lap!(timer, "Execute the call");
140
141 cast_mut_ref!(trace as Trace<N>).prepare(query)?;
143 lap!(timer, "Prepare the assignments");
144
145 let execution = trace.prove_execution::<$aleo, _>(&locator, rng)?;
147 lap!(timer, "Compute the proof");
148
149 Ok(cast_ref!(execution as Execution<N>).clone())
151 }};
152 }
153
154 let result = process!(self, logic);
156 finish!(timer, "Execute the authorization");
157 result
158 }
159
160 #[inline]
163 fn execute_fee_authorization_raw<R: Rng + CryptoRng>(
164 &self,
165 authorization: Authorization<N>,
166 query: Option<Query<N, C::BlockStorage>>,
167 rng: &mut R,
168 ) -> Result<Fee<N>> {
169 let timer = timer!("VM::execute_fee_authorization_raw");
170
171 let query = match query {
173 Some(query) => query,
174 None => Query::VM(self.block_store().clone()),
175 };
176 lap!(timer, "Prepare the query");
177
178 macro_rules! logic {
179 ($process:expr, $network:path, $aleo:path) => {{
180 let authorization = cast_ref!(authorization as Authorization<$network>);
182 let (_, mut trace) = $process.execute::<$aleo, _>(authorization.clone(), rng)?;
184 lap!(timer, "Execute the call");
185
186 cast_mut_ref!(trace as Trace<N>).prepare(query)?;
188 lap!(timer, "Prepare the assignments");
189
190 let fee = trace.prove_fee::<$aleo, _>(rng)?;
192 lap!(timer, "Compute the proof");
193
194 Ok(cast_ref!(fee as Fee<N>).clone())
196 }};
197 }
198
199 let result = process!(self, logic);
201 finish!(timer, "Execute the authorization");
202 result
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use console::{
210 account::{Address, ViewKey},
211 network::Testnet3,
212 program::{Ciphertext, Value},
213 types::Field,
214 };
215 use ledger_block::Transition;
216 use ledger_store::helpers::memory::ConsensusMemory;
217
218 use indexmap::IndexMap;
219
220 type CurrentNetwork = Testnet3;
221
222 fn prepare_vm(
223 rng: &mut TestRng,
224 ) -> Result<(
225 VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
226 IndexMap<Field<CurrentNetwork>, Record<CurrentNetwork, Ciphertext<CurrentNetwork>>>,
227 )> {
228 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
230
231 let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::<IndexMap<_, _>>();
233
234 let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
236
237 let vm = crate::vm::test_helpers::sample_vm();
239 vm.add_next_block(&genesis).unwrap();
241
242 Ok((vm, records))
243 }
244
245 #[test]
246 fn test_transfer_private_transaction_size() {
247 let rng = &mut TestRng::default();
248
249 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
251 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
252 let address = Address::try_from(&caller_private_key).unwrap();
253
254 let (vm, records) = prepare_vm(rng).unwrap();
256
257 let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
259
260 let inputs = [
262 Value::<CurrentNetwork>::Record(record),
263 Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
264 Value::<CurrentNetwork>::from_str("1u64").unwrap(),
265 ]
266 .into_iter();
267
268 let transaction =
270 vm.execute(&caller_private_key, ("credits.aleo", "transfer_private"), inputs, None, 0, None, rng).unwrap();
271
272 let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
274 assert_eq!(3629, transaction_size_in_bytes, "Update me if serialization has changed");
275
276 assert!(matches!(transaction, Transaction::Execute(_, _, _)));
278 if let Transaction::Execute(_, execution, _) = &transaction {
279 let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
280 assert_eq!(2210, execution_size_in_bytes, "Update me if serialization has changed");
281 }
282 }
283
284 #[test]
285 fn test_transfer_public_transaction_size() {
286 let rng = &mut TestRng::default();
287
288 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
290 let address = Address::try_from(&caller_private_key).unwrap();
291
292 let (vm, _) = prepare_vm(rng).unwrap();
294
295 let inputs = [
297 Value::<CurrentNetwork>::from_str(&address.to_string()).unwrap(),
298 Value::<CurrentNetwork>::from_str("1u64").unwrap(),
299 ]
300 .into_iter();
301
302 let transaction =
304 vm.execute(&caller_private_key, ("credits.aleo", "transfer_public"), inputs, None, 0, None, rng).unwrap();
305
306 let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
308 assert_eq!(2807, transaction_size_in_bytes, "Update me if serialization has changed");
309
310 assert!(matches!(transaction, Transaction::Execute(_, _, _)));
312 if let Transaction::Execute(_, execution, _) = &transaction {
313 let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
314 assert_eq!(1388, execution_size_in_bytes, "Update me if serialization has changed");
315 }
316 }
317
318 #[test]
319 fn test_join_transaction_size() {
320 let rng = &mut TestRng::default();
321
322 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
324 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
325
326 let (vm, records) = prepare_vm(rng).unwrap();
328
329 let mut records = records.values();
331 let record_1 = records.next().unwrap().decrypt(&caller_view_key).unwrap();
332 let record_2 = records.next().unwrap().decrypt(&caller_view_key).unwrap();
333
334 let inputs = [Value::<CurrentNetwork>::Record(record_1), Value::<CurrentNetwork>::Record(record_2)].into_iter();
336
337 let transaction =
339 vm.execute(&caller_private_key, ("credits.aleo", "join"), inputs, None, 0, None, rng).unwrap();
340
341 let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
343 assert_eq!(3474, transaction_size_in_bytes, "Update me if serialization has changed");
344
345 assert!(matches!(transaction, Transaction::Execute(_, _, _)));
347 if let Transaction::Execute(_, execution, _) = &transaction {
348 let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
349 assert_eq!(2055, execution_size_in_bytes, "Update me if serialization has changed");
350 }
351 }
352
353 #[test]
354 fn test_split_transaction_size() {
355 let rng = &mut TestRng::default();
356
357 let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
359 let caller_view_key = ViewKey::try_from(&caller_private_key).unwrap();
360
361 let (vm, records) = prepare_vm(rng).unwrap();
363
364 let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap();
366
367 let inputs =
369 [Value::<CurrentNetwork>::Record(record), Value::<CurrentNetwork>::from_str("1u64").unwrap()].into_iter();
370
371 let transaction =
373 vm.execute(&caller_private_key, ("credits.aleo", "split"), inputs, None, 0, None, rng).unwrap();
374
375 let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len();
377 assert_eq!(2134, transaction_size_in_bytes, "Update me if serialization has changed");
378
379 assert!(matches!(transaction, Transaction::Execute(_, _, _)));
381 if let Transaction::Execute(_, execution, _) = &transaction {
382 let execution_size_in_bytes = execution.to_bytes_le().unwrap().len();
383 assert_eq!(2099, execution_size_in_bytes, "Update me if serialization has changed");
384 }
385 }
386
387 #[test]
388 fn test_fee_private_transition_size() {
389 let rng = &mut TestRng::default();
390
391 let transaction = ledger_test_helpers::sample_fee_private_transaction(rng);
393 let fee = match transaction {
395 Transaction::Fee(_, fee) => fee,
396 _ => panic!("Expected a fee transaction"),
397 };
398 let fee_size_in_bytes = fee.to_bytes_le().unwrap().len();
400 assert_eq!(2011, fee_size_in_bytes, "Update me if serialization has changed");
401 }
402
403 #[test]
404 fn test_fee_public_transition_size() {
405 let rng = &mut TestRng::default();
406
407 let transaction = ledger_test_helpers::sample_fee_public_transaction(rng);
409 let fee = match transaction {
411 Transaction::Fee(_, fee) => fee,
412 _ => panic!("Expected a fee transaction"),
413 };
414 let fee_size_in_bytes = fee.to_bytes_le().unwrap().len();
416 assert_eq!(1384, fee_size_in_bytes, "Update me if serialization has changed");
417 }
418}