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>(
421 0,
422 0,
423 None,
424 0,
425 0,
426 Default::default(),
427 ) {
428 Ok(s) => s,
429 Err(e) => return failed(format!("Failed to build FinalizeGlobalState: {e}")),
430 };
431 let response = match catch_unwind(AssertUnwindSafe(|| {
432 vm.process().evaluate_view_at_height(state, vm.finalize_store(), program_id, function_id, parsed_inputs, 0)
433 })) {
434 Ok(Ok(resp)) => resp,
435 Ok(Err(e)) => return failed(format!("{e}")),
436 Err(e) => return failed(format!("{e:?}")),
437 };
438 let output = match response.len() {
439 0 => Value::make_unit(),
440 1 => response[0].clone().into(),
441 _ => Value::make_tuple(response.iter().map(|x| x.clone().into())),
442 };
443 EvaluationOutcome {
444 outcome: Outcome { program_name: case.program_name.clone(), function: case.function.clone(), output },
445 status: EvaluationStatus::Success,
446 }
447}
448
449fn handle_transition(
451 case: &Case,
452 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
453 program_id: ProgramID<CurrentNetwork>,
454 function_id: Identifier<CurrentNetwork>,
455 private_key: &PrivateKey<CurrentNetwork>,
456 rng: &mut ChaCha20Rng,
457) -> EvaluationOutcome {
458 let failed = |e: String| failed_evaluation_outcome(case, e);
459 let inputs = case.input.iter();
460
461 let authorization =
463 match catch_unwind(AssertUnwindSafe(|| vm.authorize(private_key, program_id, function_id, inputs, rng))) {
464 Ok(Ok(auth)) => auth,
465 Ok(Err(e)) => return failed(format!("{e}")),
466 Err(e) => return failed(format!("{e:?}")),
467 };
468
469 let response = match catch_unwind(AssertUnwindSafe(|| vm.process().evaluate::<AleoTestnetV0>(authorization))) {
471 Ok(Ok(resp)) => resp,
472 Ok(Err(e)) => return failed(format!("{e}")),
473 Err(e) => return failed(format!("{e:?}")),
474 };
475
476 let outputs = response.outputs();
477 let output = match outputs.len() {
478 0 => Value::make_unit(),
479 1 => outputs[0].clone().into(),
480 _ => Value::make_tuple(outputs.iter().map(|x| x.clone().into())),
481 };
482
483 EvaluationOutcome {
484 outcome: Outcome { program_name: case.program_name.clone(), function: case.function.clone(), output },
485 status: EvaluationStatus::Success,
486 }
487}
488
489fn failed_evaluation_outcome(case: &Case, e: String) -> EvaluationOutcome {
491 EvaluationOutcome {
492 outcome: Outcome {
493 program_name: case.program_name.clone(),
494 function: case.function.clone(),
495 output: Value::make_unit(),
496 },
497 status: EvaluationStatus::Failed(e),
498 }
499}
500
501pub fn run_with_ledger(config: &Config, case_sets: &[Vec<Case>]) -> Result<Vec<Vec<ExecutionOutcome>>> {
503 if case_sets.is_empty() {
504 return Ok(Vec::new());
505 }
506
507 let mut rng = ChaCha20Rng::seed_from_u64(config.seed);
509
510 let genesis_private_key = PrivateKey::from_str(TEST_PRIVATE_KEY).unwrap();
512
513 let mut blocks = Vec::new();
515
516 let genesis_block =
518 Block::from_bytes_le(include_bytes!("resources/genesis_8d710d7e2_40val_snarkos_dev_network.bin"))?;
519
520 let ledger = Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(
523 genesis_block.clone(),
524 StorageMode::new_test(None),
525 )
526 .unwrap();
527
528 let latest_consensus_version = ConsensusVersion::latest();
530 let start_height =
531 config.start_height.unwrap_or(CurrentNetwork::CONSENSUS_HEIGHT(latest_consensus_version).unwrap());
532 while ledger.latest_height() < start_height {
533 let block = ledger
534 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], vec![], &mut rng)
535 .map_err(|_| anyhow!("Failed to prepare advance to next beacon block"))?;
536 ledger.advance_to_next_block(&block).map_err(|_| anyhow!("Failed to advance to next block"))?;
537 blocks.push(block);
538 }
539
540 for Program { bytecode, name } in &config.programs {
542 let aleo_program =
545 ProgramCore::from_str(bytecode).map_err(|e| anyhow!("Failed to parse bytecode of program {name}: {e}"))?;
546
547 let mut deploy = |edition: u16| -> Result<()> {
548 let deployment = if config.skip_proving {
549 deploy_without_proof(
550 ledger.vm(),
551 &genesis_private_key,
552 &aleo_program,
553 edition,
554 latest_consensus_version,
555 &mut rng,
556 )
557 .map_err(|e| anyhow!("Failed to deploy program {name}: {e}"))?
558 } else {
559 ledger
562 .vm()
563 .deploy(&genesis_private_key, &aleo_program, None, 0, None, &mut rng)
564 .map_err(|e| anyhow!("Failed to deploy program {name}: {e}"))?
565 };
566 let block = ledger
567 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], vec![deployment], &mut rng)
568 .map_err(|e| anyhow!("Failed to prepare to advance block for program {name}: {e}"))?;
569 ledger
570 .advance_to_next_block(&block)
571 .map_err(|e| anyhow!("Failed to advance block for program {name}: {e}"))?;
572
573 if block.transactions().num_accepted() != 1 {
575 return Err(anyhow!("Deployment transaction for program {name} not accepted.").into());
576 }
577
578 blocks.push(block);
580
581 Ok(())
582 };
583
584 deploy(0)?;
586 if !aleo_program.contains_constructor() {
588 deploy(1)?;
589 }
590 }
591
592 let mut indexed_ledgers = vec![(0, ledger)];
594 indexed_ledgers.extend(
595 (1..case_sets.len())
596 .map(|i| {
597 let l = Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(
600 genesis_block.clone(),
601 StorageMode::new_test(None),
602 )
603 .expect("Failed to load copy of ledger");
604 for block in blocks.iter() {
606 l.advance_to_next_block(block).expect("Failed to add setup block to ledger");
607 }
608
609 (i, l)
610 })
611 .collect::<Vec<_>>(),
612 );
613
614 let results = indexed_ledgers
616 .into_iter()
617 .map(|(index, ledger)| {
618 let cases = &case_sets[index];
620 let mut rng = rng.clone();
622
623 let skip_proving = config.skip_proving;
625 let transactions: Vec<Transaction<CurrentNetwork>> = cases
626 .iter()
627 .filter_map(|case| case.private_key.as_ref())
628 .map(|key| {
629 let private_key =
631 PrivateKey::<CurrentNetwork>::from_str(key).expect("Failed to parse private key.");
632 let address = Address::try_from(private_key).expect("Failed to convert private key to address.");
634 if skip_proving {
636 let (tx, _) = execute_without_proof(
637 ledger.vm(),
638 &genesis_private_key,
639 "credits.aleo",
640 "transfer_public",
641 [
642 SvmValue::from_str(&format!("{address}")).expect("Failed to parse recipient address"),
643 SvmValue::from_str("1_000_000_000_000u64").expect("Failed to parse amount"),
644 ]
645 .iter(),
646 latest_consensus_version,
647 &mut rng,
648 )
649 .expect("Failed to generate funding transaction");
650 tx
651 } else {
652 ledger
653 .vm()
654 .execute(
655 &genesis_private_key,
656 ("credits.aleo", "transfer_public"),
657 [
658 SvmValue::from_str(&format!("{address}"))
659 .expect("Failed to parse recipient address"),
660 SvmValue::from_str("1_000_000_000_000u64").expect("Failed to parse amount"),
661 ]
662 .iter(),
663 None,
664 0u64,
665 None,
666 &mut rng,
667 )
668 .expect("Failed to generate funding transaction")
669 }
670 })
671 .collect();
672
673 let block = ledger
675 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], transactions, &mut rng)
676 .expect("Failed to prepare advance to next beacon block");
677 assert!(block.aborted_transaction_ids().is_empty());
679 assert_eq!(block.transactions().num_rejected(), 0);
680 ledger.advance_to_next_block(&block).expect("Failed to advance to next block");
682
683 let mut case_outcomes = Vec::new();
684
685 for case in cases {
686 assert!(
687 ledger.vm().contains_program(&ProgramID::from_str(&case.program_name).unwrap()),
688 "Program {} should exist.",
689 case.program_name
690 );
691
692 let private_key = case
693 .private_key
694 .as_ref()
695 .map(|key| PrivateKey::from_str(key).expect("Failed to parse private key."))
696 .unwrap_or(genesis_private_key);
697
698 let mut execution = None;
699 let mut verified = false;
700 let mut status = ExecutionStatus::None;
701 let mut abort_reason: Option<String> = None;
702
703 let execute_output = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
707 if skip_proving {
708 execute_without_proof(
709 ledger.vm(),
710 &private_key,
711 &case.program_name,
712 &case.function,
713 case.input.iter(),
714 latest_consensus_version,
715 &mut rng,
716 )
717 } else {
718 ledger
719 .vm()
720 .execute_with_response(
721 &private_key,
722 (&case.program_name, &case.function),
723 case.input.iter(),
724 None,
725 0,
726 None,
727 &mut rng,
728 )
729 .map_err(anyhow::Error::from)
730 }
731 }));
732
733 if let Err(payload) = execute_output {
734 let s1 = payload.downcast_ref::<&str>().map(|s| s.to_string());
735 let s2 = payload.downcast_ref::<String>().cloned();
736 let s = s1.or(s2).unwrap_or_else(|| "Unknown panic payload".to_string());
737
738 case_outcomes.push(ExecutionOutcome {
739 outcome: Outcome {
740 program_name: case.program_name.clone(),
741 function: case.function.clone(),
742 output: Value::make_unit(),
743 },
744 status: ExecutionStatus::Halted(s),
745 verified: false,
746 execution: "".to_string(),
747 });
748
749 continue;
750 }
751
752 let result = execute_output.unwrap().and_then(|(transaction, response)| {
753 verified = skip_proving || ledger.vm().check_transaction(&transaction, None, &mut rng).is_ok();
756 execution = Some(transaction.clone());
757 let block = ledger
758 .prepare_advance_to_next_beacon_block(&private_key, vec![], vec![], vec![transaction], &mut rng)
759 .map_err(|e| anyhow::anyhow!("{e}"))?;
760 status =
761 match (block.aborted_transaction_ids().is_empty(), block.transactions().num_accepted() == 1) {
762 (false, _) => {
763 if let Some(ref tx) = execution
765 && let Err(e) = ledger.vm().check_transaction(tx, None, &mut rng)
766 {
767 abort_reason = Some(format!("{e}"));
768 }
769 ExecutionStatus::Aborted(abort_reason.take())
770 }
771 (true, true) => ExecutionStatus::Accepted,
772 (true, false) => ExecutionStatus::Rejected,
773 };
774 ledger.advance_to_next_block(&block)?;
775 Ok(response)
776 });
777
778 let output = match result {
779 Ok(response) => {
780 let outputs = response.outputs();
781 match outputs.len() {
782 0 => Value::make_unit(),
783 1 => outputs[0].clone().into(),
784 _ => Value::make_tuple(outputs.iter().map(|x| x.clone().into())),
785 }
786 }
787 Err(e) => Value::make_string(format!("Failed to extract output: {e}")),
788 };
789
790 let execution = if let Some(Transaction::Execute(_, _, execution, _)) = execution {
793 Some(Execution::from(execution.into_transitions(), Default::default(), None).unwrap())
794 } else {
795 None
796 };
797
798 case_outcomes.push(ExecutionOutcome {
799 outcome: Outcome {
800 program_name: case.program_name.clone(),
801 function: case.function.clone(),
802 output,
803 },
804 status,
805 verified,
806 execution: serde_json::to_string_pretty(&execution).expect("Serialization failure"),
807 });
808 }
809
810 Ok((index, case_outcomes))
811 })
812 .collect::<Result<Vec<_>>>()?;
813
814 let mut ordered_results: Vec<Vec<ExecutionOutcome>> = vec![Default::default(); case_sets.len()];
816 for (index, outcomes) in results.into_iter() {
817 ordered_results[index] = outcomes;
818 }
819
820 Ok(ordered_results)
821}