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    /// Flag that allows sending funds to inexistent accounts.
65    ///
66    /// Disabled by default, to better signal missing accounts.
67    pub fn insert_ghost_accounts(mut self) -> Self {
68        let vm = &mut self.get_mut_debugger_backend().vm_runner.blockchain_mock.vm;
69        vm.set_insert_ghost_accounts(true);
70        self
71    }
72
73    pub fn vm_go() -> Self {
74        ScenarioWorld {
75            current_dir: std::env::current_dir().unwrap(),
76            backend: Backend::VmGoBackend,
77        }
78    }
79
80    /// Runs a scenario file (`.scen.json`) with the configured backend.
81    ///
82    /// Will crash and produce an output if the test failed for any reason.
83    pub fn run<P: AsRef<Path>>(self, relative_path: P) {
84        let mut absolute_path = self.current_dir.clone();
85        absolute_path.push(relative_path);
86        match self.backend {
87            Backend::Debugger(mut debugger) => {
88                debugger.run_scenario_file(&absolute_path);
89            }
90            Backend::VmGoBackend => {
91                run_mx_scenario_go(&absolute_path);
92            }
93        }
94    }
95
96    pub(crate) fn get_debugger_backend(&self) -> &DebuggerBackend {
97        if let Backend::Debugger(debugger) = &self.backend {
98            debugger
99        } else {
100            panic!("operation only available for the contract debugger backend")
101        }
102    }
103
104    pub(crate) fn get_mut_debugger_backend(&mut self) -> &mut DebuggerBackend {
105        if let Backend::Debugger(debugger) = &mut self.backend {
106            debugger
107        } else {
108            panic!("operation only available for the contract debugger backend")
109        }
110    }
111
112    pub(crate) fn get_state(&self) -> &BlockchainState {
113        &self.get_debugger_backend().vm_runner.blockchain_mock.state
114    }
115
116    pub(crate) fn get_mut_state(&mut self) -> &mut BlockchainState {
117        &mut self
118            .get_mut_debugger_backend()
119            .vm_runner
120            .blockchain_mock
121            .state
122    }
123
124    pub fn start_trace(&mut self) -> &mut Self {
125        self.get_mut_debugger_backend().trace = Some(ScenarioTrace::default());
126        self
127    }
128
129    /// Tells the tests where the crate lies relative to the workspace.
130    /// This ensures that the paths are set correctly, including in debug mode.
131    pub fn set_current_dir_from_workspace(&mut self, relative_path: &str) -> &mut Self {
132        let mut path = find_current_workspace().unwrap();
133        path.push(relative_path);
134        self.current_dir = path;
135        self
136    }
137
138    pub fn current_dir(&self) -> &PathBuf {
139        &self.current_dir
140    }
141
142    /// Exports current scenario to a JSON file, as created.
143    pub fn write_scenario_trace<P: AsRef<Path>>(&mut self, file_path: P) {
144        if let Some(trace) = &mut self.get_mut_debugger_backend().trace {
145            trace.write_scenario_trace(file_path);
146        } else {
147            panic!("scenario trace no initialized")
148        }
149    }
150
151    #[deprecated(
152        since = "0.39.0",
153        note = "Renamed, use `write_scenario_trace` instead."
154    )]
155    pub fn write_mandos_trace<P: AsRef<Path>>(&mut self, file_path: P) {
156        self.write_scenario_trace(file_path);
157    }
158
159    #[cfg(feature = "bls")]
160    pub fn create_aggregated_signature(
161        &mut self,
162        pk_size: usize,
163        message: &[u8],
164    ) -> Result<
165        (multiversx_chain_vm::G1, Vec<multiversx_chain_vm::G2>),
166        multiversx_chain_vm::BlsError,
167    > {
168        multiversx_chain_vm::crypto_functions_bls::create_aggregated_signature(pk_size, message)
169    }
170}