multiversx_sc_scenario/facade/
scenario_world.rs

1use multiversx_chain_vm::{blockchain::state::BlockchainState, schedule::GasScheduleVersion};
2
3use crate::{
4    scenario::{
5        run_trace::ScenarioTrace,
6        run_vm::{ExecutorConfig, ScenarioVMRunner},
7    },
8    vm_go_tool::run_mx_scenario_go,
9};
10use multiversx_sc_meta_lib::tools::find_current_workspace;
11use std::path::{Path, PathBuf};
12
13use super::debugger_backend::DebuggerBackend;
14
15/// A facade for contracts tests.
16///
17/// Contains all the context needed to execute scenarios involving contracts.
18///
19/// Currently defers most of the operations to the blockchain mock object directly,
20/// but that one will be refactored and broken up into smaller pieces.
21pub struct ScenarioWorld {
22    pub(crate) current_dir: PathBuf,
23    pub(crate) backend: Backend,
24}
25
26pub(crate) enum Backend {
27    Debugger(Box<DebuggerBackend>),
28    VmGoBackend,
29}
30
31impl Default for ScenarioWorld {
32    fn default() -> Self {
33        Self::debugger()
34    }
35}
36
37impl ScenarioWorld {
38    pub fn debugger() -> Self {
39        ScenarioWorld {
40            current_dir: std::env::current_dir().unwrap(),
41            backend: Backend::Debugger(Box::new(DebuggerBackend {
42                vm_runner: ScenarioVMRunner::new(),
43                trace: None,
44            })),
45        }
46    }
47
48    /// Backwards compatibility only.
49    pub fn new() -> Self {
50        Self::debugger()
51    }
52
53    pub fn executor_config(mut self, config: ExecutorConfig) -> Self {
54        self.get_mut_debugger_backend().vm_runner.executor_config = config;
55        self
56    }
57
58    pub fn gas_schedule(mut self, gas_schedule: GasScheduleVersion) -> Self {
59        let vm = &mut self.get_mut_debugger_backend().vm_runner.blockchain_mock.vm;
60        vm.change_gas_schedule(gas_schedule.load_gas_schedule());
61        self
62    }
63
64    pub fn vm_go() -> Self {
65        ScenarioWorld {
66            current_dir: std::env::current_dir().unwrap(),
67            backend: Backend::VmGoBackend,
68        }
69    }
70
71    /// Runs a scenario file (`.scen.json`) with the configured backend.
72    ///
73    /// Will crash and produce an output if the test failed for any reason.
74    pub fn run<P: AsRef<Path>>(self, relative_path: P) {
75        let mut absolute_path = self.current_dir.clone();
76        absolute_path.push(relative_path);
77        match self.backend {
78            Backend::Debugger(mut debugger) => {
79                debugger.run_scenario_file(&absolute_path);
80            }
81            Backend::VmGoBackend => {
82                run_mx_scenario_go(&absolute_path);
83            }
84        }
85    }
86
87    pub(crate) fn get_debugger_backend(&self) -> &DebuggerBackend {
88        if let Backend::Debugger(debugger) = &self.backend {
89            debugger
90        } else {
91            panic!("operation only available for the contract debugger backend")
92        }
93    }
94
95    pub(crate) fn get_mut_debugger_backend(&mut self) -> &mut DebuggerBackend {
96        if let Backend::Debugger(debugger) = &mut self.backend {
97            debugger
98        } else {
99            panic!("operation only available for the contract debugger backend")
100        }
101    }
102
103    pub(crate) fn get_state(&self) -> &BlockchainState {
104        &self.get_debugger_backend().vm_runner.blockchain_mock.state
105    }
106
107    pub(crate) fn get_mut_state(&mut self) -> &mut BlockchainState {
108        &mut self
109            .get_mut_debugger_backend()
110            .vm_runner
111            .blockchain_mock
112            .state
113    }
114
115    pub fn start_trace(&mut self) -> &mut Self {
116        self.get_mut_debugger_backend().trace = Some(ScenarioTrace::default());
117        self
118    }
119
120    /// Tells the tests where the crate lies relative to the workspace.
121    /// This ensures that the paths are set correctly, including in debug mode.
122    pub fn set_current_dir_from_workspace(&mut self, relative_path: &str) -> &mut Self {
123        let mut path = find_current_workspace().unwrap();
124        path.push(relative_path);
125        self.current_dir = path;
126        self
127    }
128
129    pub fn current_dir(&self) -> &PathBuf {
130        &self.current_dir
131    }
132
133    /// Exports current scenario to a JSON file, as created.
134    pub fn write_scenario_trace<P: AsRef<Path>>(&mut self, file_path: P) {
135        if let Some(trace) = &mut self.get_mut_debugger_backend().trace {
136            trace.write_scenario_trace(file_path);
137        } else {
138            panic!("scenario trace no initialized")
139        }
140    }
141
142    #[deprecated(
143        since = "0.39.0",
144        note = "Renamed, use `write_scenario_trace` instead."
145    )]
146    pub fn write_mandos_trace<P: AsRef<Path>>(&mut self, file_path: P) {
147        self.write_scenario_trace(file_path);
148    }
149
150    #[cfg(feature = "bls")]
151    pub fn create_aggregated_signature(
152        &mut self,
153        pk_size: usize,
154        message: &[u8],
155    ) -> Result<
156        (multiversx_chain_vm::G1, Vec<multiversx_chain_vm::G2>),
157        multiversx_chain_vm::BlsError,
158    > {
159        multiversx_chain_vm::crypto_functions_bls::create_aggregated_signature(pk_size, message)
160    }
161}