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 cell::Cell,
67 fmt,
68 panic::{AssertUnwindSafe, catch_unwind},
69 str::FromStr as _,
70};
71
72type CurrentNetwork = TestnetV0;
73
74thread_local! {
75 static HALT_EXPECTED: Cell<bool> = const { Cell::new(false) };
76}
77
78pub fn halt_expected() -> bool {
80 HALT_EXPECTED.with(Cell::get)
81}
82
83#[derive(Debug)]
85pub struct Config {
86 pub seed: u64,
87 pub start_height: Option<u32>,
89 pub programs: Vec<Program>,
90 pub skip_proving: bool,
92}
93
94#[derive(Clone, Debug, Default)]
96pub struct Program {
97 pub bytecode: String,
98 pub name: String,
99}
100
101#[derive(Clone, Debug)]
106pub struct SeedMapping {
107 pub mapping: String,
109 pub key: String,
111 pub value: String,
113}
114
115#[derive(Clone, Debug, Default)]
117pub struct Case {
118 pub program_name: String,
119 pub function: String,
120 pub private_key: Option<String>,
121 pub input: Vec<String>,
122 pub seed_mapping: Vec<SeedMapping>,
124}
125
126#[derive(Clone, Debug, PartialEq, Eq)]
128pub enum ExecutionStatus {
129 None,
130 Aborted(Option<String>),
131 Accepted,
132 Rejected,
133 Halted(String),
134}
135
136impl fmt::Display for ExecutionStatus {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match self {
139 Self::Halted(s) => write!(f, "halted ({s})"),
140 Self::None => write!(f, "none"),
141 Self::Aborted(None) => write!(f, "aborted"),
142 Self::Aborted(Some(reason)) => write!(f, "aborted: {reason}"),
143 Self::Accepted => write!(f, "accepted"),
144 Self::Rejected => write!(f, "rejected"),
145 }
146 }
147}
148
149#[derive(Debug, Clone)]
150pub enum EvaluationStatus {
151 Success,
152 Failed(String),
153}
154
155impl fmt::Display for EvaluationStatus {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 Self::Success => write!(f, "success"),
159 Self::Failed(e) => write!(f, "failed: {e}"),
160 }
161 }
162}
163
164#[derive(Debug, Clone)]
166pub struct Outcome {
167 pub program_name: String,
168 pub function: String,
169 pub output: Value,
170}
171
172impl Outcome {
173 pub fn output(&self) -> Value {
174 self.output.clone()
175 }
176}
177
178#[derive(Debug, Clone)]
180pub struct EvaluationOutcome {
181 pub outcome: Outcome,
182 pub status: EvaluationStatus,
183}
184
185impl EvaluationOutcome {
186 pub fn output(&self) -> Value {
187 self.outcome.output()
188 }
189}
190
191#[derive(Debug, Clone)]
193pub struct ExecutionOutcome {
194 pub outcome: Outcome,
195 pub verified: bool,
196 pub execution: String,
197 pub status: ExecutionStatus,
198}
199
200impl ExecutionOutcome {
201 pub fn output(&self) -> Value {
202 self.outcome.output()
203 }
204}
205
206pub const PLACEHOLDER_VK: &str = "verifier1q9qqqqqqqqqqqqyvxgqqqqqqqqq87vsqqqqqqqqqhe7sqqqqqqqqqma4qqqqqqqqqq65yqqqqqqqqqqvqqqqqqqqqqqgtlaj49fmrk2d8slmselaj9tpucgxv6awu6yu4pfcn5xa0yy0tpxpc8wemasjvvxr9248vt3509vpk3u60ejyfd9xtvjmudpp7ljq2csk4yqz70ug3x8xp3xn3ul0yrrw0mvd2g8ju7rts50u3smue03gp99j88f0ky8h6fjlpvh58rmxv53mldmgrxa3fq6spsh8gt5whvsyu2rk4a2wmeyrgvvdf29pwp02srktxnvht3k6ff094usjtllggva2ym75xc4lzuqu9xx8ylfkm3qc7lf7ktk9uu9du5raukh828dzgq26hrarq5ajjl7pz7zk924kekjrp92r6jh9dpp05mxtuffwlmvew84dvnqrkre7lw29mkdzgdxwe7q8z0vnkv2vwwdraekw2va3plu7rkxhtnkuxvce0qkgxcxn5mtg9q2c3vxdf2r7jjse2g68dgvyh85q4mzfnvn07lletrpty3vypus00gfu9m47rzay4mh5w9f03z9zgzgzhkv0mupdqsk8naljqm9tc2qqzhf6yp3mnv2ey89xk7sw9pslzzlkndfd2upzmew4e4vnrkr556kexs9qrykkuhsr260mnrgh7uv0sp2meky0keeukaxgjdsnmy77kl48g3swcvqdjm50ejzr7x04vy7hn7anhd0xeetclxunnl7pd6e52qxdlr3nmutz4zr8f2xqa57a2zkl59a28w842cj4783zpy9hxw03k6vz4a3uu7sm072uqknpxjk8fyq4vxtqd08kd93c2mt40lj9ag35nm4rwcfjayejk57m9qqu83qnkrj3sz90pw808srmf705n2yu6gvqazpvu2mwm8x6mgtlsntxfhr0qas43rqxnccft36z4ygty86390t7vrt08derz8368z8ekn3yywxgp4uq24gm6e58tpp0lcvtpsm3nkwpnmzztx4qvkaf6vk38wg787h8mfpqqqqqqqqqqffkful";
208
209pub const PLACEHOLDER_CERT: &str =
211 "certificate1qyqsqqqqqqqqqqxvwszp09v860w62s2l4g6eqf0kzppyax5we36957ywqm2dplzwvvlqg0kwlnmhzfatnax7uaqt7yqqqw0sc4u";
212
213fn deploy_without_proof(
215 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
216 private_key: &PrivateKey<CurrentNetwork>,
217 program: &ProgramCore<CurrentNetwork>,
218 edition: u16,
219 consensus_version: ConsensusVersion,
220 rng: &mut ChaCha20Rng,
221) -> anyhow::Result<Transaction<CurrentNetwork>> {
222 let placeholder_vk = VerifyingKey::from_str(PLACEHOLDER_VK)?;
225 let placeholder_cert = Certificate::from_str(PLACEHOLDER_CERT)?;
226 let verifying_keys = program
227 .functions()
228 .keys()
229 .chain(program.records().keys())
230 .map(|name| (*name, (placeholder_vk.clone(), placeholder_cert.clone())))
231 .collect::<Vec<_>>();
232
233 let mut deployment = Deployment::new(edition, program.clone(), verifying_keys, None, None)
235 .map_err(|e| anyhow!("Failed to create deployment: {e}"))?;
236
237 deployment.set_program_owner_raw(Some(Address::try_from(private_key)?));
239 deployment.set_program_checksum_raw(Some(deployment.program().to_checksum()));
240
241 let deployment_id = deployment.to_deployment_id()?;
243 let owner = ProgramOwner::new(private_key, deployment_id, rng)?;
244
245 let (minimum_deployment_cost, _) = deployment_cost(vm.process(), &deployment, consensus_version)?;
247
248 let fee_authorization = vm.authorize_fee_public(private_key, minimum_deployment_cost, 0, deployment_id, rng)?;
250
251 let state_root = vm.block_store().current_state_root();
253 let fee = Fee::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None)?;
254
255 Transaction::from_deployment(owner, deployment, fee).map_err(|e| anyhow!("Failed to create deployment tx: {e}"))
256}
257
258fn execute_without_proof(
260 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
261 private_key: &PrivateKey<CurrentNetwork>,
262 program_id: &str,
263 function_name: &str,
264 inputs: impl ExactSizeIterator<Item = impl TryInto<SvmValue<CurrentNetwork>>>,
265 consensus_version: ConsensusVersion,
266 rng: &mut ChaCha20Rng,
267) -> anyhow::Result<(Transaction<CurrentNetwork>, snarkvm::prelude::Response<CurrentNetwork>)> {
268 let authorization = vm.authorize(private_key, program_id, function_name, inputs, rng)?;
270
271 let response = vm.process().evaluate::<AleoTestnetV0>(authorization.clone())?;
273
274 let state_root = vm.block_store().current_state_root();
276 let execution = Execution::from(authorization.transitions().values().cloned(), state_root, None)?;
277
278 let (cost, _) = execution_cost(vm.process(), &execution, consensus_version)?;
280
281 let execution_id = authorization.to_execution_id()?;
283 let fee_authorization = vm.authorize_fee_public(private_key, cost, 0, execution_id, rng)?;
284 let fee = Fee::from(fee_authorization.transitions().into_iter().next().unwrap().1, state_root, None)?;
285
286 let transaction = Transaction::from_execution(execution, Some(fee))?;
287 Ok((transaction, response))
288}
289
290pub fn run_without_ledger(config: &Config, cases: &[Case]) -> Result<Vec<EvaluationOutcome>> {
296 if cases.is_empty() {
298 return Ok(Vec::new());
299 }
300
301 let programs_and_editions: Vec<(snarkvm::prelude::Program<CurrentNetwork>, u16)> = config
302 .programs
303 .iter()
304 .map(|Program { bytecode, name }| {
305 let program = snarkvm::prelude::Program::<CurrentNetwork>::from_str(bytecode)
306 .map_err(|e| anyhow!("Failed to parse bytecode of program {name}: {e}"))?;
307 let edition: u16 = 1;
309 Ok((program, edition))
310 })
311 .collect::<Result<Vec<_>>>()?;
312
313 let outcomes: Vec<EvaluationOutcome> = cases
314 .iter()
315 .map(|case| {
316 let rng = &mut ChaCha20Rng::seed_from_u64(config.seed);
317
318 let failed_outcome = |e: String| EvaluationOutcome {
320 outcome: Outcome {
321 program_name: case.program_name.clone(),
322 function: case.function.clone(),
323 output: Value::make_unit(),
324 },
325 status: EvaluationStatus::Failed(e),
326 };
327
328 let vm = match ConsensusStore::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::open(
329 StorageMode::Production,
330 ) {
331 Ok(store) => match VM::from(store) {
332 Ok(vm) => vm,
333 Err(e) => return failed_outcome(format!("VM init error: {e}")),
334 },
335 Err(e) => return failed_outcome(format!("Consensus store open error: {e}")),
336 };
337
338 if let Err(e) = vm.process().lock().add_programs_with_editions(&programs_and_editions) {
339 return failed_outcome(format!("Failed to add programs: {e}"));
340 }
341
342 for (program, _) in &programs_and_editions {
347 for mapping_name in program.mappings().keys() {
348 let _ = vm.finalize_store().initialize_mapping(*program.id(), *mapping_name);
352 }
353 }
354
355 let private_key = match PrivateKey::from_str(leo_ast::TEST_PRIVATE_KEY) {
356 Ok(pk) => pk,
357 Err(e) => return failed_outcome(format!("Private key parse error: {e}")),
358 };
359 let program_id = match ProgramID::<CurrentNetwork>::from_str(&case.program_name) {
360 Ok(pid) => pid,
361 Err(e) => return failed_outcome(format!("ProgramID parse error: {e}")),
362 };
363 let function_id = match Identifier::<CurrentNetwork>::from_str(&case.function) {
364 Ok(fid) => fid,
365 Err(e) => return failed_outcome(format!("FunctionID parse error: {e}")),
366 };
367
368 for SeedMapping { mapping: mapping_name_str, key: key_str, value: value_str } in &case.seed_mapping {
372 let mapping_name = match Identifier::<CurrentNetwork>::from_str(mapping_name_str) {
373 Ok(n) => n,
374 Err(e) => return failed_outcome(format!("Failed to parse seed mapping name: {e}")),
375 };
376 let key = match snarkvm::prelude::Plaintext::<CurrentNetwork>::from_str(key_str) {
377 Ok(k) => k,
378 Err(e) => return failed_outcome(format!("Failed to parse seed key: {e}")),
379 };
380 let value = match SvmValue::<CurrentNetwork>::from_str(value_str) {
381 Ok(v) => v,
382 Err(e) => return failed_outcome(format!("Failed to parse seed value: {e}")),
383 };
384 if let Err(e) = vm.finalize_store().update_key_value(program_id, mapping_name, key, value) {
385 return failed_outcome(format!("Failed to seed mapping: {e}"));
386 }
387 }
388
389 let is_view = vm
394 .process()
395 .get_stack(program_id)
396 .map(|stack| stack.program().contains_view(&function_id))
397 .unwrap_or(false);
398
399 if is_view {
400 handle_view(case, &vm, program_id, function_id)
401 } else {
402 handle_transition(case, &vm, program_id, function_id, &private_key, rng)
403 }
404 })
405 .collect();
406
407 Ok(outcomes)
408}
409
410fn handle_view(
412 case: &Case,
413 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
414 program_id: ProgramID<CurrentNetwork>,
415 function_id: Identifier<CurrentNetwork>,
416) -> EvaluationOutcome {
417 let failed = |e: String| failed_evaluation_outcome(case, e);
418 let parsed_inputs: Vec<SvmValue<CurrentNetwork>> = match case
419 .input
420 .iter()
421 .map(|s| SvmValue::<CurrentNetwork>::from_str(s))
422 .collect::<std::result::Result<Vec<_>, _>>()
423 {
424 Ok(v) => v,
425 Err(e) => return failed(format!("Failed to parse view input: {e}")),
426 };
427 let state = match snarkvm::synthesizer::program::FinalizeGlobalState::new::<CurrentNetwork>(
434 0,
435 0,
436 Some(1234567890i64),
437 0,
438 0,
439 Default::default(),
440 None,
441 None,
442 ) {
443 Ok(s) => s,
444 Err(e) => return failed(format!("Failed to build FinalizeGlobalState: {e}")),
445 };
446 let registered = match vm.process().get_stack(program_id) {
450 Ok(stack) => stack,
451 Err(e) => return failed(format!("Failed to load stack for `{program_id}`: {e}")),
452 };
453 let mut stack = match snarkvm::synthesizer::process::Stack::new(vm.process(), registered.program()) {
454 Ok(stack) => stack,
455 Err(e) => return failed(format!("Failed to build stack for `{program_id}`: {e}")),
456 };
457 match PrivateKey::<CurrentNetwork>::from_str(leo_ast::TEST_PRIVATE_KEY)
458 .and_then(|pk| Address::<CurrentNetwork>::try_from(&pk))
459 {
460 Ok(owner) => stack.set_program_owner(Some(owner)),
461 Err(e) => return failed(format!("Failed to derive program owner: {e}")),
462 };
463 let response = match catch_unwind(AssertUnwindSafe(|| {
464 snarkvm::synthesizer::process::evaluate_view_with_stack_at_height(
465 state,
466 vm.finalize_store(),
467 &stack,
468 &function_id,
469 parsed_inputs,
470 0,
471 )
472 })) {
473 Ok(Ok(resp)) => resp,
474 Ok(Err(e)) => return failed(format!("{e}")),
475 Err(e) => return failed(format!("{e:?}")),
476 };
477 let output = match response.len() {
478 0 => Value::make_unit(),
479 1 => response[0].clone().into(),
480 _ => Value::make_tuple(response.iter().map(|x| x.clone().into())),
481 };
482 EvaluationOutcome {
483 outcome: Outcome { program_name: case.program_name.clone(), function: case.function.clone(), output },
484 status: EvaluationStatus::Success,
485 }
486}
487
488fn handle_transition(
490 case: &Case,
491 vm: &VM<CurrentNetwork, ConsensusMemory<CurrentNetwork>>,
492 program_id: ProgramID<CurrentNetwork>,
493 function_id: Identifier<CurrentNetwork>,
494 private_key: &PrivateKey<CurrentNetwork>,
495 rng: &mut ChaCha20Rng,
496) -> EvaluationOutcome {
497 let failed = |e: String| failed_evaluation_outcome(case, e);
498 let inputs = case.input.iter();
499
500 let authorization =
502 match catch_unwind(AssertUnwindSafe(|| vm.authorize(private_key, program_id, function_id, inputs, rng))) {
503 Ok(Ok(auth)) => auth,
504 Ok(Err(e)) => return failed(format!("{e}")),
505 Err(e) => return failed(format!("{e:?}")),
506 };
507
508 let response = match catch_unwind(AssertUnwindSafe(|| vm.process().evaluate::<AleoTestnetV0>(authorization))) {
510 Ok(Ok(resp)) => resp,
511 Ok(Err(e)) => return failed(format!("{e}")),
512 Err(e) => return failed(format!("{e:?}")),
513 };
514
515 let outputs = response.outputs();
516 let output = match outputs.len() {
517 0 => Value::make_unit(),
518 1 => outputs[0].clone().into(),
519 _ => Value::make_tuple(outputs.iter().map(|x| x.clone().into())),
520 };
521
522 EvaluationOutcome {
523 outcome: Outcome { program_name: case.program_name.clone(), function: case.function.clone(), output },
524 status: EvaluationStatus::Success,
525 }
526}
527
528fn failed_evaluation_outcome(case: &Case, e: String) -> EvaluationOutcome {
530 EvaluationOutcome {
531 outcome: Outcome {
532 program_name: case.program_name.clone(),
533 function: case.function.clone(),
534 output: Value::make_unit(),
535 },
536 status: EvaluationStatus::Failed(e),
537 }
538}
539
540pub fn run_with_ledger(
542 config: &Config,
543 case_sets: &[Vec<Case>],
544 mut on_case_set_done: impl FnMut(usize, &[ExecutionOutcome]),
545) -> Result<Vec<Vec<ExecutionOutcome>>> {
546 if case_sets.is_empty() {
547 return Ok(Vec::new());
548 }
549
550 let mut rng = ChaCha20Rng::seed_from_u64(config.seed);
552
553 let genesis_private_key = PrivateKey::from_str(TEST_PRIVATE_KEY).unwrap();
555
556 let mut blocks = Vec::new();
558
559 let genesis_block =
561 Block::from_bytes_le(include_bytes!("resources/genesis_8d710d7e2_40val_snarkos_dev_network.bin"))?;
562
563 let ledger = Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(
566 genesis_block.clone(),
567 StorageMode::new_test(None),
568 )
569 .unwrap();
570
571 let latest_consensus_version = ConsensusVersion::latest();
573 let start_height =
574 config.start_height.unwrap_or(CurrentNetwork::CONSENSUS_HEIGHT(latest_consensus_version).unwrap());
575 while ledger.latest_height() < start_height {
576 let block = ledger
577 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], vec![], &mut rng)
578 .map_err(|_| anyhow!("Failed to prepare advance to next beacon block"))?;
579 ledger.advance_to_next_block(&block).map_err(|_| anyhow!("Failed to advance to next block"))?;
580 blocks.push(block);
581 }
582
583 for Program { bytecode, name } in &config.programs {
585 let aleo_program =
588 ProgramCore::from_str(bytecode).map_err(|e| anyhow!("Failed to parse bytecode of program {name}: {e}"))?;
589
590 let mut deploy = |edition: u16| -> Result<()> {
591 let deployment = if config.skip_proving {
592 deploy_without_proof(
593 ledger.vm(),
594 &genesis_private_key,
595 &aleo_program,
596 edition,
597 latest_consensus_version,
598 &mut rng,
599 )
600 .map_err(|e| anyhow!("Failed to deploy program {name}: {e}"))?
601 } else {
602 ledger
605 .vm()
606 .deploy(&genesis_private_key, &aleo_program, None, 0, None, &mut rng)
607 .map_err(|e| anyhow!("Failed to deploy program {name}: {e}"))?
608 };
609 let block = ledger
610 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], vec![deployment], &mut rng)
611 .map_err(|e| anyhow!("Failed to prepare to advance block for program {name}: {e}"))?;
612 ledger
613 .advance_to_next_block(&block)
614 .map_err(|e| anyhow!("Failed to advance block for program {name}: {e}"))?;
615
616 if block.transactions().num_accepted() != 1 {
618 return Err(anyhow!("Deployment transaction for program {name} not accepted.").into());
619 }
620
621 blocks.push(block);
623
624 Ok(())
625 };
626
627 deploy(0)?;
629 if !aleo_program.contains_constructor() {
631 deploy(1)?;
632 }
633 }
634
635 let mut original_ledger = Some(ledger);
637 case_sets
638 .iter()
639 .enumerate()
640 .map(|(index, cases)| {
641 let ledger = if index == 0 {
645 original_ledger.take().expect("ledger 0 is built exactly once")
646 } else {
647 let ledger = Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(
648 genesis_block.clone(),
649 StorageMode::new_test(None),
650 )
651 .expect("Failed to load copy of ledger");
652 for block in &blocks {
653 ledger.advance_to_next_block(block).expect("Failed to add setup block to ledger");
654 }
655 ledger
656 };
657
658 let mut rng = rng.clone();
660
661 let skip_proving = config.skip_proving;
663 let transactions: Vec<Transaction<CurrentNetwork>> = cases
664 .iter()
665 .filter_map(|case| case.private_key.as_ref())
666 .map(|key| {
667 let private_key =
669 PrivateKey::<CurrentNetwork>::from_str(key).expect("Failed to parse private key.");
670 let address = Address::try_from(private_key).expect("Failed to convert private key to address.");
672 if skip_proving {
674 let (tx, _) = execute_without_proof(
675 ledger.vm(),
676 &genesis_private_key,
677 "credits.aleo",
678 "transfer_public",
679 [
680 SvmValue::from_str(&format!("{address}")).expect("Failed to parse recipient address"),
681 SvmValue::from_str("1_000_000_000_000u64").expect("Failed to parse amount"),
682 ]
683 .iter(),
684 latest_consensus_version,
685 &mut rng,
686 )
687 .expect("Failed to generate funding transaction");
688 tx
689 } else {
690 ledger
691 .vm()
692 .execute(
693 &genesis_private_key,
694 ("credits.aleo", "transfer_public"),
695 [
696 SvmValue::from_str(&format!("{address}"))
697 .expect("Failed to parse recipient address"),
698 SvmValue::from_str("1_000_000_000_000u64").expect("Failed to parse amount"),
699 ]
700 .iter(),
701 None,
702 0u64,
703 None,
704 &mut rng,
705 )
706 .expect("Failed to generate funding transaction")
707 }
708 })
709 .collect();
710
711 let block = ledger
713 .prepare_advance_to_next_beacon_block(&genesis_private_key, vec![], vec![], transactions, &mut rng)
714 .expect("Failed to prepare advance to next beacon block");
715 assert!(block.aborted_transaction_ids().is_empty());
717 assert_eq!(block.transactions().num_rejected(), 0);
718 ledger.advance_to_next_block(&block).expect("Failed to advance to next block");
720
721 let mut case_outcomes = Vec::new();
722
723 for case in cases {
724 assert!(
725 ledger.vm().contains_program(&ProgramID::from_str(&case.program_name).unwrap()),
726 "Program {} should exist.",
727 case.program_name
728 );
729
730 let private_key = case
731 .private_key
732 .as_ref()
733 .map(|key| PrivateKey::from_str(key).expect("Failed to parse private key."))
734 .unwrap_or(genesis_private_key);
735
736 let mut execution = None;
737 let mut verified = false;
738 let mut status = ExecutionStatus::None;
739 let mut abort_reason: Option<String> = None;
740
741 let execute_output = HALT_EXPECTED.with(|expected| {
743 let previous = expected.replace(true);
744 let result = catch_unwind(AssertUnwindSafe(|| {
745 if skip_proving {
746 execute_without_proof(
747 ledger.vm(),
748 &private_key,
749 &case.program_name,
750 &case.function,
751 case.input.iter(),
752 latest_consensus_version,
753 &mut rng,
754 )
755 } else {
756 ledger
757 .vm()
758 .execute_with_response(
759 &private_key,
760 (&case.program_name, &case.function),
761 case.input.iter(),
762 None,
763 0,
764 None,
765 &mut rng,
766 )
767 .map_err(anyhow::Error::from)
768 }
769 }));
770 expected.set(previous);
771 result
772 });
773
774 if let Err(payload) = execute_output {
775 let s1 = payload.downcast_ref::<&str>().map(|s| s.to_string());
776 let s2 = payload.downcast_ref::<String>().cloned();
777 let s = s1.or(s2).unwrap_or_else(|| "Unknown panic payload".to_string());
778
779 case_outcomes.push(ExecutionOutcome {
780 outcome: Outcome {
781 program_name: case.program_name.clone(),
782 function: case.function.clone(),
783 output: Value::make_unit(),
784 },
785 status: ExecutionStatus::Halted(s),
786 verified: false,
787 execution: "".to_string(),
788 });
789
790 continue;
791 }
792
793 let result = execute_output.unwrap().and_then(|(transaction, response)| {
794 verified = skip_proving || ledger.vm().check_transaction(&transaction, None, &mut rng).is_ok();
797 execution = Some(transaction.clone());
798 let block = ledger
799 .prepare_advance_to_next_beacon_block(&private_key, vec![], vec![], vec![transaction], &mut rng)
800 .map_err(|e| anyhow::anyhow!("{e}"))?;
801 status =
802 match (block.aborted_transaction_ids().is_empty(), block.transactions().num_accepted() == 1) {
803 (false, _) => {
804 if let Some(ref tx) = execution
806 && let Err(e) = ledger.vm().check_transaction(tx, None, &mut rng)
807 {
808 abort_reason = Some(format!("{e}"));
809 }
810 ExecutionStatus::Aborted(abort_reason.take())
811 }
812 (true, true) => ExecutionStatus::Accepted,
813 (true, false) => ExecutionStatus::Rejected,
814 };
815 ledger.advance_to_next_block(&block)?;
816 Ok(response)
817 });
818
819 let output = match result {
820 Ok(response) => {
821 let outputs = response.outputs();
822 match outputs.len() {
823 0 => Value::make_unit(),
824 1 => outputs[0].clone().into(),
825 _ => Value::make_tuple(outputs.iter().map(|x| x.clone().into())),
826 }
827 }
828 Err(e) => Value::make_string(format!("Failed to extract output: {e}")),
829 };
830
831 let execution = if let Some(Transaction::Execute(_, _, execution, _)) = execution {
834 Some(Execution::from(execution.into_transitions(), Default::default(), None).unwrap())
835 } else {
836 None
837 };
838
839 case_outcomes.push(ExecutionOutcome {
840 outcome: Outcome {
841 program_name: case.program_name.clone(),
842 function: case.function.clone(),
843 output,
844 },
845 status,
846 verified,
847 execution: serde_json::to_string_pretty(&execution).expect("Serialization failure"),
848 });
849 }
850
851 on_case_set_done(index, &case_outcomes);
852
853 Ok(case_outcomes)
854 })
855 .collect()
856}