1use leo_ast::{TEST_PRIVATE_KEY, const_eval::Value};
31use leo_errors::Result;
32
33use aleo_std_storage::StorageMode;
34use anyhow::anyhow;
35use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng as _};
36use serde_json;
37use snarkvm::{
38 circuit::AleoTestnetV0,
39 prelude::{
40 Address,
41 Block,
42 Certificate,
43 ConsensusVersion,
44 Deployment,
45 Execution,
46 Fee,
47 FromBytes,
48 Identifier,
49 Ledger,
50 Network,
51 PrivateKey,
52 ProgramID,
53 ProgramOwner,
54 TestnetV0,
55 Transaction,
56 VM,
57 Value as SvmValue,
58 VerifyingKey,
59 deployment_cost,
60 execution_cost,
61 store::{ConsensusStore, helpers::memory::ConsensusMemory},
62 },
63 synthesizer::program::{FinalizeStoreTrait, ProgramCore, StackTrait},
64};
65use std::{
66 fmt,
67 panic::{AssertUnwindSafe, catch_unwind},
68 str::FromStr as _,
69};
70
71type CurrentNetwork = TestnetV0;
72
73#[derive(Debug)]
75pub struct Config {
76 pub seed: u64,
77 pub start_height: Option<u32>,
79 pub programs: Vec<Program>,
80 pub skip_proving: bool,
82}
83
84#[derive(Clone, Debug, Default)]
86pub struct Program {
87 pub bytecode: String,
88 pub name: String,
89}
90
91#[derive(Clone, Debug)]
96pub struct SeedMapping {
97 pub mapping: String,
99 pub key: String,
101 pub value: String,
103}
104
105#[derive(Clone, Debug, Default)]
107pub struct Case {
108 pub program_name: String,
109 pub function: String,
110 pub private_key: Option<String>,
111 pub input: Vec<String>,
112 pub seed_mapping: Vec<SeedMapping>,
114}
115
116#[derive(Clone, Debug, PartialEq, Eq)]
118pub enum ExecutionStatus {
119 None,
120 Aborted(Option<String>),
121 Accepted,
122 Rejected,
123 Halted(String),
124}
125
126impl fmt::Display for ExecutionStatus {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match self {
129 Self::Halted(s) => write!(f, "halted ({s})"),
130 Self::None => write!(f, "none"),
131 Self::Aborted(None) => write!(f, "aborted"),
132 Self::Aborted(Some(reason)) => write!(f, "aborted: {reason}"),
133 Self::Accepted => write!(f, "accepted"),
134 Self::Rejected => write!(f, "rejected"),
135 }
136 }
137}
138
139#[derive(Debug, Clone)]
140pub enum EvaluationStatus {
141 Success,
142 Failed(String),
143}
144
145impl fmt::Display for EvaluationStatus {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 match self {
148 Self::Success => write!(f, "success"),
149 Self::Failed(e) => write!(f, "failed: {e}"),
150 }
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct Outcome {
157 pub program_name: String,
158 pub function: String,
159 pub output: Value,
160}
161
162impl Outcome {
163 pub fn output(&self) -> Value {
164 self.output.clone()
165 }
166}
167
168#[derive(Debug, Clone)]
170pub struct EvaluationOutcome {
171 pub outcome: Outcome,
172 pub status: EvaluationStatus,
173}
174
175impl EvaluationOutcome {
176 pub fn output(&self) -> Value {
177 self.outcome.output()
178 }
179}
180
181#[derive(Debug, Clone)]
183pub struct ExecutionOutcome {
184 pub outcome: Outcome,
185 pub verified: bool,
186 pub execution: String,
187 pub status: ExecutionStatus,
188}
189
190impl ExecutionOutcome {
191 pub fn output(&self) -> Value {
192 self.outcome.output()
193 }
194}
195
196pub const PLACEHOLDER_VK: &str = "verifier1q9qqqqqqqqqqqqyvxgqqqqqqqqq87vsqqqqqqqqqhe7sqqqqqqqqqma4qqqqqqqqqq65yqqqqqqqqqqvqqqqqqqqqqqgtlaj49fmrk2d8slmselaj9tpucgxv6awu6yu4pfcn5xa0yy0tpxpc8wemasjvvxr9248vt3509vpk3u60ejyfd9xtvjmudpp7ljq2csk4yqz70ug3x8xp3xn3ul0yrrw0mvd2g8ju7rts50u3smue03gp99j88f0ky8h6fjlpvh58rmxv53mldmgrxa3fq6spsh8gt5whvsyu2rk4a2wmeyrgvvdf29pwp02srktxnvht3k6ff094usjtllggva2ym75xc4lzuqu9xx8ylfkm3qc7lf7ktk9uu9du5raukh828dzgq26hrarq5ajjl7pz7zk924kekjrp92r6jh9dpp05mxtuffwlmvew84dvnqrkre7lw29mkdzgdxwe7q8z0vnkv2vwwdraekw2va3plu7rkxhtnkuxvce0qkgxcxn5mtg9q2c3vxdf2r7jjse2g68dgvyh85q4mzfnvn07lletrpty3vypus00gfu9m47rzay4mh5w9f03z9zgzgzhkv0mupdqsk8naljqm9tc2qqzhf6yp3mnv2ey89xk7sw9pslzzlkndfd2upzmew4e4vnrkr556kexs9qrykkuhsr260mnrgh7uv0sp2meky0keeukaxgjdsnmy77kl48g3swcvqdjm50ejzr7x04vy7hn7anhd0xeetclxunnl7pd6e52qxdlr3nmutz4zr8f2xqa57a2zkl59a28w842cj4783zpy9hxw03k6vz4a3uu7sm072uqknpxjk8fyq4vxtqd08kd93c2mt40lj9ag35nm4rwcfjayejk57m9qqu83qnkrj3sz90pw808srmf705n2yu6gvqazpvu2mwm8x6mgtlsntxfhr0qas43rqxnccft36z4ygty86390t7vrt08derz8368z8ekn3yywxgp4uq24gm6e58tpp0lcvtpsm3nkwpnmzztx4qvkaf6vk38wg787h8mfpqqqqqqqqqqffkful";
198
199pub const PLACEHOLDER_CERT: &str =
201 "certificate1qyqsqqqqqqqqqqxvwszp09v860w62s2l4g6eqf0kzppyax5we36957ywqm2dplzwvvlqg0kwlnmhzfatnax7uaqt7yqqqw0sc4u";
202
203fn deploy_without_proof(
205 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
206 private_key: &PrivateKey<CurrentNetwork>,
207 program: &ProgramCore<CurrentNetwork>,
208 edition: u16,
209 consensus_version: ConsensusVersion,
210 rng: &mut ChaCha20Rng,
211) -> anyhow::Result<Transaction<CurrentNetwork>> {
212 let placeholder_vk = VerifyingKey::from_str(PLACEHOLDER_VK)?;
215 let placeholder_cert = Certificate::from_str(PLACEHOLDER_CERT)?;
216 let verifying_keys = program
217 .functions()
218 .keys()
219 .chain(program.records().keys())
220 .map(|name| (*name, (placeholder_vk.clone(), placeholder_cert.clone())))
221 .collect::<Vec<_>>();
222
223 let mut deployment = Deployment::new(edition, program.clone(), verifying_keys, None, None)
225 .map_err(|e| anyhow!("Failed to create deployment: {e}"))?;
226
227 deployment.set_program_owner_raw(Some(Address::try_from(private_key)?));
229 deployment.set_program_checksum_raw(Some(deployment.program().to_checksum()));
230
231 let deployment_id = deployment.to_deployment_id()?;
233 let owner = ProgramOwner::new(private_key, deployment_id, rng)?;
234
235 let (minimum_deployment_cost, _) = deployment_cost(vm.process(), &deployment, consensus_version)?;
237
238 let fee_authorization = vm.authorize_fee_public(private_key, minimum_deployment_cost, 0, deployment_id, rng)?;
240
241 let state_root = vm.block_store().current_state_root();
243 let fee = Fee::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None)?;
244
245 Transaction::from_deployment(owner, deployment, fee).map_err(|e| anyhow!("Failed to create deployment tx: {e}"))
246}
247
248fn execute_without_proof(
250 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
251 private_key: &PrivateKey<CurrentNetwork>,
252 program_id: &str,
253 function_name: &str,
254 inputs: impl ExactSizeIterator<Item = impl TryInto<SvmValue<CurrentNetwork>>>,
255 consensus_version: ConsensusVersion,
256 rng: &mut ChaCha20Rng,
257) -> anyhow::Result<(Transaction<CurrentNetwork>, snarkvm::prelude::Response<CurrentNetwork>)> {
258 let authorization = vm.authorize(private_key, program_id, function_name, inputs, rng)?;
260
261 let response = vm.process().evaluate::<AleoTestnetV0>(authorization.clone())?;
263
264 let state_root = vm.block_store().current_state_root();
266 let execution = Execution::from(authorization.transitions().values().cloned(), state_root, None)?;
267
268 let (cost, _) = execution_cost(vm.process(), &execution, consensus_version)?;
270
271 let execution_id = authorization.to_execution_id()?;
273 let fee_authorization = vm.authorize_fee_public(private_key, cost, 0, execution_id, rng)?;
274 let fee = Fee::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None)?;
275
276 let transaction = Transaction::from_execution(execution, Some(fee))?;
277 Ok((transaction, response))
278}
279
280pub fn run_without_ledger(config: &Config, cases: &[Case]) -> Result<Vec<EvaluationOutcome>> {
286 if cases.is_empty() {
288 return Ok(Vec::new());
289 }
290
291 let programs_and_editions: Vec<(snarkvm::prelude::Program<CurrentNetwork>, u16)> = config
292 .programs
293 .iter()
294 .map(|Program { bytecode, name }| {
295 let program = snarkvm::prelude::Program::<CurrentNetwork>::from_str(bytecode)
296 .map_err(|e| anyhow!("Failed to parse bytecode of program {name}: {e}"))?;
297 let edition: u16 = 1;
299 Ok((program, edition))
300 })
301 .collect::<Result<Vec<_>>>()?;
302
303 let outcomes: Vec<EvaluationOutcome> = cases
304 .iter()
305 .map(|case| {
306 let rng = &mut ChaCha20Rng::seed_from_u64(config.seed);
307
308 let failed_outcome = |e: String| EvaluationOutcome {
310 outcome: Outcome {
311 program_name: case.program_name.clone(),
312 function: case.function.clone(),
313 output: Value::make_unit(),
314 },
315 status: EvaluationStatus::Failed(e),
316 };
317
318 let vm = match ConsensusStore::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::open(
319 StorageMode::Production,
320 ) {
321 Ok(store) => match VM::from(store) {
322 Ok(vm) => vm,
323 Err(e) => return failed_outcome(format!("VM init error: {e}")),
324 },
325 Err(e) => return failed_outcome(format!("Consensus store open error: {e}")),
326 };
327
328 if let Err(e) = vm.process().lock().add_programs_with_editions(&programs_and_editions) {
329 return failed_outcome(format!("Failed to add programs: {e}"));
330 }
331
332 for (program, _) in &programs_and_editions {
337 for mapping_name in program.mappings().keys() {
338 let _ = vm.finalize_store().initialize_mapping(*program.id(), *mapping_name);
342 }
343 }
344
345 let private_key = match PrivateKey::from_str(leo_ast::TEST_PRIVATE_KEY) {
346 Ok(pk) => pk,
347 Err(e) => return failed_outcome(format!("Private key parse error: {e}")),
348 };
349 let program_id = match ProgramID::<CurrentNetwork>::from_str(&case.program_name) {
350 Ok(pid) => pid,
351 Err(e) => return failed_outcome(format!("ProgramID parse error: {e}")),
352 };
353 let function_id = match Identifier::<CurrentNetwork>::from_str(&case.function) {
354 Ok(fid) => fid,
355 Err(e) => return failed_outcome(format!("FunctionID parse error: {e}")),
356 };
357
358 for SeedMapping { mapping: mapping_name_str, key: key_str, value: value_str } in &case.seed_mapping {
362 let mapping_name = match Identifier::<CurrentNetwork>::from_str(mapping_name_str) {
363 Ok(n) => n,
364 Err(e) => return failed_outcome(format!("Failed to parse seed mapping name: {e}")),
365 };
366 let key = match snarkvm::prelude::Plaintext::<CurrentNetwork>::from_str(key_str) {
367 Ok(k) => k,
368 Err(e) => return failed_outcome(format!("Failed to parse seed key: {e}")),
369 };
370 let value = match SvmValue::<CurrentNetwork>::from_str(value_str) {
371 Ok(v) => v,
372 Err(e) => return failed_outcome(format!("Failed to parse seed value: {e}")),
373 };
374 if let Err(e) = vm.finalize_store().update_key_value(program_id, mapping_name, key, value) {
375 return failed_outcome(format!("Failed to seed mapping: {e}"));
376 }
377 }
378
379 let is_view = vm
384 .process()
385 .get_stack(program_id)
386 .map(|stack| stack.program().contains_view(&function_id))
387 .unwrap_or(false);
388
389 if is_view {
390 handle_view(case, &vm, program_id, function_id)
391 } else {
392 handle_transition(case, &vm, program_id, function_id, &private_key, rng)
393 }
394 })
395 .collect();
396
397 Ok(outcomes)
398}
399
400fn handle_view(
402 case: &Case,
403 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
404 program_id: ProgramID<CurrentNetwork>,
405 function_id: Identifier<CurrentNetwork>,
406) -> EvaluationOutcome {
407 let failed = |e: String| failed_evaluation_outcome(case, e);
408 let parsed_inputs: Vec<SvmValue<CurrentNetwork>> = match case
409 .input
410 .iter()
411 .map(|s| SvmValue::<CurrentNetwork>::from_str(s))
412 .collect::<std::result::Result<Vec<_>, _>>()
413 {
414 Ok(v) => v,
415 Err(e) => return failed(format!("Failed to parse view input: {e}")),
416 };
417 let state = match snarkvm::synthesizer::program::FinalizeGlobalState::new::<CurrentNetwork>(
424 0,
425 0,
426 Some(1234567890i64),
427 0,
428 0,
429 Default::default(),
430 None,
431 ) {
432 Ok(s) => s,
433 Err(e) => return failed(format!("Failed to build FinalizeGlobalState: {e}")),
434 };
435 let registered = match vm.process().get_stack(program_id) {
439 Ok(stack) => stack,
440 Err(e) => return failed(format!("Failed to load stack for `{program_id}`: {e}")),
441 };
442 let mut stack = match snarkvm::synthesizer::process::Stack::new(vm.process(), registered.program()) {
443 Ok(stack) => stack,
444 Err(e) => return failed(format!("Failed to build stack for `{program_id}`: {e}")),
445 };
446 match PrivateKey::<CurrentNetwork>::from_str(leo_ast::TEST_PRIVATE_KEY)
447 .and_then(|pk| Address::<CurrentNetwork>::try_from(&pk))
448 {
449 Ok(owner) => stack.set_program_owner(Some(owner)),
450 Err(e) => return failed(format!("Failed to derive program owner: {e}")),
451 };
452 let response = match catch_unwind(AssertUnwindSafe(|| {
453 snarkvm::synthesizer::process::evaluate_view_with_stack_at_height(
454 state,
455 vm.finalize_store(),
456 &stack,
457 &function_id,
458 parsed_inputs,
459 0,
460 )
461 })) {
462 Ok(Ok(resp)) => resp,
463 Ok(Err(e)) => return failed(format!("{e}")),
464 Err(e) => return failed(format!("{e:?}")),
465 };
466 let output = match response.len() {
467 0 => Value::make_unit(),
468 1 => response[0].clone().into(),
469 _ => Value::make_tuple(response.iter().map(|x| x.clone().into())),
470 };
471 EvaluationOutcome {
472 outcome: Outcome { program_name: case.program_name.clone(), function: case.function.clone(), output },
473 status: EvaluationStatus::Success,
474 }
475}
476
477fn handle_transition(
479 case: &Case,
480 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
481 program_id: ProgramID<CurrentNetwork>,
482 function_id: Identifier<CurrentNetwork>,
483 private_key: &PrivateKey<CurrentNetwork>,
484 rng: &mut ChaCha20Rng,
485) -> EvaluationOutcome {
486 let failed = |e: String| failed_evaluation_outcome(case, e);
487 let inputs = case.input.iter();
488
489 let authorization =
491 match catch_unwind(AssertUnwindSafe(|| vm.authorize(private_key, program_id, function_id, inputs, rng))) {
492 Ok(Ok(auth)) => auth,
493 Ok(Err(e)) => return failed(format!("{e}")),
494 Err(e) => return failed(format!("{e:?}")),
495 };
496
497 let response = match catch_unwind(AssertUnwindSafe(|| vm.process().evaluate::<AleoTestnetV0>(authorization))) {
499 Ok(Ok(resp)) => resp,
500 Ok(Err(e)) => return failed(format!("{e}")),
501 Err(e) => return failed(format!("{e:?}")),
502 };
503
504 let outputs = response.outputs();
505 let output = match outputs.len() {
506 0 => Value::make_unit(),
507 1 => outputs[0].clone().into(),
508 _ => Value::make_tuple(outputs.iter().map(|x| x.clone().into())),
509 };
510
511 EvaluationOutcome {
512 outcome: Outcome { program_name: case.program_name.clone(), function: case.function.clone(), output },
513 status: EvaluationStatus::Success,
514 }
515}
516
517fn failed_evaluation_outcome(case: &Case, e: String) -> EvaluationOutcome {
519 EvaluationOutcome {
520 outcome: Outcome {
521 program_name: case.program_name.clone(),
522 function: case.function.clone(),
523 output: Value::make_unit(),
524 },
525 status: EvaluationStatus::Failed(e),
526 }
527}
528
529pub fn run_with_ledger(config: &Config, case_sets: &[Vec<Case>]) -> Result<Vec<Vec<ExecutionOutcome>>> {
531 if case_sets.is_empty() {
532 return Ok(Vec::new());
533 }
534
535 let mut rng = ChaCha20Rng::seed_from_u64(config.seed);
537
538 let genesis_private_key = PrivateKey::from_str(TEST_PRIVATE_KEY).unwrap();
540
541 let mut blocks = Vec::new();
543
544 let genesis_block =
546 Block::from_bytes_le(include_bytes!("resources/genesis_8d710d7e2_40val_snarkos_dev_network.bin"))?;
547
548 let ledger = Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(
551 genesis_block.clone(),
552 StorageMode::new_test(None),
553 )
554 .unwrap();
555
556 let latest_consensus_version = ConsensusVersion::latest();
558 let start_height =
559 config.start_height.unwrap_or(CurrentNetwork::CONSENSUS_HEIGHT(latest_consensus_version).unwrap());
560 while ledger.latest_height() < start_height {
561 let block = ledger
562 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], vec![], &mut rng)
563 .map_err(|_| anyhow!("Failed to prepare advance to next beacon block"))?;
564 ledger.advance_to_next_block(&block).map_err(|_| anyhow!("Failed to advance to next block"))?;
565 blocks.push(block);
566 }
567
568 for Program { bytecode, name } in &config.programs {
570 let aleo_program =
573 ProgramCore::from_str(bytecode).map_err(|e| anyhow!("Failed to parse bytecode of program {name}: {e}"))?;
574
575 let mut deploy = |edition: u16| -> Result<()> {
576 let deployment = if config.skip_proving {
577 deploy_without_proof(
578 ledger.vm(),
579 &genesis_private_key,
580 &aleo_program,
581 edition,
582 latest_consensus_version,
583 &mut rng,
584 )
585 .map_err(|e| anyhow!("Failed to deploy program {name}: {e}"))?
586 } else {
587 ledger
590 .vm()
591 .deploy(&genesis_private_key, &aleo_program, None, 0, None, &mut rng)
592 .map_err(|e| anyhow!("Failed to deploy program {name}: {e}"))?
593 };
594 let block = ledger
595 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], vec![deployment], &mut rng)
596 .map_err(|e| anyhow!("Failed to prepare to advance block for program {name}: {e}"))?;
597 ledger
598 .advance_to_next_block(&block)
599 .map_err(|e| anyhow!("Failed to advance block for program {name}: {e}"))?;
600
601 if block.transactions().num_accepted() != 1 {
603 return Err(anyhow!("Deployment transaction for program {name} not accepted.").into());
604 }
605
606 blocks.push(block);
608
609 Ok(())
610 };
611
612 deploy(0)?;
614 if !aleo_program.contains_constructor() {
616 deploy(1)?;
617 }
618 }
619
620 let mut indexed_ledgers = vec![(0, ledger)];
622 indexed_ledgers.extend(
623 (1..case_sets.len())
624 .map(|i| {
625 let l = Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(
628 genesis_block.clone(),
629 StorageMode::new_test(None),
630 )
631 .expect("Failed to load copy of ledger");
632 for block in blocks.iter() {
634 l.advance_to_next_block(block).expect("Failed to add setup block to ledger");
635 }
636
637 (i, l)
638 })
639 .collect::<Vec<_>>(),
640 );
641
642 let results = indexed_ledgers
644 .into_iter()
645 .map(|(index, ledger)| {
646 let cases = &case_sets[index];
648 let mut rng = rng.clone();
650
651 let skip_proving = config.skip_proving;
653 let transactions: Vec<Transaction<CurrentNetwork>> = cases
654 .iter()
655 .filter_map(|case| case.private_key.as_ref())
656 .map(|key| {
657 let private_key =
659 PrivateKey::<CurrentNetwork>::from_str(key).expect("Failed to parse private key.");
660 let address = Address::try_from(private_key).expect("Failed to convert private key to address.");
662 if skip_proving {
664 let (tx, _) = execute_without_proof(
665 ledger.vm(),
666 &genesis_private_key,
667 "credits.aleo",
668 "transfer_public",
669 [
670 SvmValue::from_str(&format!("{address}")).expect("Failed to parse recipient address"),
671 SvmValue::from_str("1_000_000_000_000u64").expect("Failed to parse amount"),
672 ]
673 .iter(),
674 latest_consensus_version,
675 &mut rng,
676 )
677 .expect("Failed to generate funding transaction");
678 tx
679 } else {
680 ledger
681 .vm()
682 .execute(
683 &genesis_private_key,
684 ("credits.aleo", "transfer_public"),
685 [
686 SvmValue::from_str(&format!("{address}"))
687 .expect("Failed to parse recipient address"),
688 SvmValue::from_str("1_000_000_000_000u64").expect("Failed to parse amount"),
689 ]
690 .iter(),
691 None,
692 0u64,
693 None,
694 &mut rng,
695 )
696 .expect("Failed to generate funding transaction")
697 }
698 })
699 .collect();
700
701 let block = ledger
703 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], transactions, &mut rng)
704 .expect("Failed to prepare advance to next beacon block");
705 assert!(block.aborted_transaction_ids().is_empty());
707 assert_eq!(block.transactions().num_rejected(), 0);
708 ledger.advance_to_next_block(&block).expect("Failed to advance to next block");
710
711 let mut case_outcomes = Vec::new();
712
713 for case in cases {
714 assert!(
715 ledger.vm().contains_program(&ProgramID::from_str(&case.program_name).unwrap()),
716 "Program {} should exist.",
717 case.program_name
718 );
719
720 let private_key = case
721 .private_key
722 .as_ref()
723 .map(|key| PrivateKey::from_str(key).expect("Failed to parse private key."))
724 .unwrap_or(genesis_private_key);
725
726 let mut execution = None;
727 let mut verified = false;
728 let mut status = ExecutionStatus::None;
729 let mut abort_reason: Option<String> = None;
730
731 let execute_output = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
735 if skip_proving {
736 execute_without_proof(
737 ledger.vm(),
738 &private_key,
739 &case.program_name,
740 &case.function,
741 case.input.iter(),
742 latest_consensus_version,
743 &mut rng,
744 )
745 } else {
746 ledger
747 .vm()
748 .execute_with_response(
749 &private_key,
750 (&case.program_name, &case.function),
751 case.input.iter(),
752 None,
753 0,
754 None,
755 &mut rng,
756 )
757 .map_err(anyhow::Error::from)
758 }
759 }));
760
761 if let Err(payload) = execute_output {
762 let s1 = payload.downcast_ref::<&str>().map(|s| s.to_string());
763 let s2 = payload.downcast_ref::<String>().cloned();
764 let s = s1.or(s2).unwrap_or_else(|| "Unknown panic payload".to_string());
765
766 case_outcomes.push(ExecutionOutcome {
767 outcome: Outcome {
768 program_name: case.program_name.clone(),
769 function: case.function.clone(),
770 output: Value::make_unit(),
771 },
772 status: ExecutionStatus::Halted(s),
773 verified: false,
774 execution: "".to_string(),
775 });
776
777 continue;
778 }
779
780 let result = execute_output.unwrap().and_then(|(transaction, response)| {
781 verified = skip_proving || ledger.vm().check_transaction(&transaction, None, &mut rng).is_ok();
784 execution = Some(transaction.clone());
785 let block = ledger
786 .prepare_advance_to_next_beacon_block(&private_key, vec![], vec![], vec![transaction], &mut rng)
787 .map_err(|e| anyhow::anyhow!("{e}"))?;
788 status =
789 match (block.aborted_transaction_ids().is_empty(), block.transactions().num_accepted() == 1) {
790 (false, _) => {
791 if let Some(ref tx) = execution
793 && let Err(e) = ledger.vm().check_transaction(tx, None, &mut rng)
794 {
795 abort_reason = Some(format!("{e}"));
796 }
797 ExecutionStatus::Aborted(abort_reason.take())
798 }
799 (true, true) => ExecutionStatus::Accepted,
800 (true, false) => ExecutionStatus::Rejected,
801 };
802 ledger.advance_to_next_block(&block)?;
803 Ok(response)
804 });
805
806 let output = match result {
807 Ok(response) => {
808 let outputs = response.outputs();
809 match outputs.len() {
810 0 => Value::make_unit(),
811 1 => outputs[0].clone().into(),
812 _ => Value::make_tuple(outputs.iter().map(|x| x.clone().into())),
813 }
814 }
815 Err(e) => Value::make_string(format!("Failed to extract output: {e}")),
816 };
817
818 let execution = if let Some(Transaction::Execute(_, _, execution, _)) = execution {
821 Some(Execution::from(execution.into_transitions(), Default::default(), None).unwrap())
822 } else {
823 None
824 };
825
826 case_outcomes.push(ExecutionOutcome {
827 outcome: Outcome {
828 program_name: case.program_name.clone(),
829 function: case.function.clone(),
830 output,
831 },
832 status,
833 verified,
834 execution: serde_json::to_string_pretty(&execution).expect("Serialization failure"),
835 });
836 }
837
838 Ok((index, case_outcomes))
839 })
840 .collect::<Result<Vec<_>>>()?;
841
842 let mut ordered_results: Vec<Vec<ExecutionOutcome>> = vec![Default::default(); case_sets.len()];
844 for (index, outcomes) in results.into_iter() {
845 ordered_results[index] = outcomes;
846 }
847
848 Ok(ordered_results)
849}