1use crate::{
22 BackendExternalities,
23 error::{
24 ActorTerminationReason, BackendAllocSyscallError, BackendSyscallError, RunFallibleError,
25 TerminationReason,
26 },
27 funcs::FuncsHandler,
28 memory::{BackendMemory, ExecutorMemory},
29 state::{HostState, State},
30};
31use alloc::{collections::BTreeSet, format, string::String};
32use core::{fmt::Debug, marker::Send};
33use gear_core::{
34 env::{Externalities, WasmEntryPoint},
35 gas::GasAmount,
36 message::DispatchKind,
37 pages::WasmPagesAmount,
38};
39use gear_lazy_pages_common::{GlobalsAccessConfig, GlobalsAccessMod};
40use gear_sandbox::{
41 AsContextExt, HostFuncType, ReturnValue, SandboxEnvironmentBuilder, SandboxInstance,
42 SandboxMemory, SandboxStore, TryFromValue, Value,
43 default_executor::{EnvironmentDefinitionBuilder, Instance, Store},
44};
45use gear_wasm_instrument::{
46 GLOBAL_NAME_GAS,
47 syscalls::SyscallName::{self, *},
48};
49#[cfg(feature = "std")]
50use {
51 gear_core::limited::LimitedStr, gear_core::memory::HostPointer,
52 gear_lazy_pages_common::GlobalsAccessError, gear_lazy_pages_common::GlobalsAccessor,
53};
54
55#[rustfmt::skip]
59macro_rules! wrap_syscall {
60 ($func:ident, $syscall:ident) => {
61 |caller, args| FuncsHandler::execute(caller, args, FuncsHandler::$func, $syscall)
62 };
63}
64
65fn store_host_state_mut<Ext: Send + 'static>(
66 store: &mut Store<HostState<Ext, BackendMemory<ExecutorMemory>>>,
67) -> &mut State<Ext, BackendMemory<ExecutorMemory>> {
68 store.data_mut().as_mut().unwrap_or_else(|| {
69 let err_msg =
70 "store_host_state_mut: State is not set, but it must be set in `Environment::new`";
71
72 log::error!("{err_msg}");
73 unreachable!("{err_msg}")
74 })
75}
76
77pub type EnvironmentExecutionResult<Ext> = Result<BackendReport<Ext>, EnvironmentError>;
78
79#[derive(Debug, derive_more::Display)]
80pub enum EnvironmentError {
81 #[display("Actor backend error: {_1}")]
82 Actor(GasAmount, String),
83 #[display("System backend error: {_0}")]
84 System(SystemEnvironmentError),
85}
86
87#[derive(Debug, derive_more::Display)]
88pub enum SystemEnvironmentError {
89 #[display("Failed to create env memory: {_0:?}")]
90 CreateEnvMemory(gear_sandbox::Error),
91 #[display("Gas counter not found or has wrong type")]
92 WrongInjectedGas,
93}
94
95pub struct Environment<Ext, EntryPoint = DispatchKind>
97where
98 Ext: BackendExternalities,
99 EntryPoint: WasmEntryPoint,
100{
101 instance: Instance<HostState<Ext, BackendMemory<ExecutorMemory>>>,
102 entries: BTreeSet<DispatchKind>,
103 entry_point: EntryPoint,
104 store: Store<HostState<Ext, BackendMemory<ExecutorMemory>>>,
105 memory: BackendMemory<ExecutorMemory>,
106}
107
108pub struct BackendReport<Ext>
109where
110 Ext: Externalities + 'static,
111{
112 pub termination_reason: TerminationReason,
113 pub store: Store<HostState<Ext, BackendMemory<ExecutorMemory>>>,
114 pub memory: BackendMemory<ExecutorMemory>,
115 pub ext: Ext,
116}
117
118struct EnvBuilder<Ext: BackendExternalities> {
121 env_def_builder: EnvironmentDefinitionBuilder<HostState<Ext, BackendMemory<ExecutorMemory>>>,
122 funcs_count: usize,
123}
124
125impl<Ext> EnvBuilder<Ext>
126where
127 Ext: BackendExternalities + Send + 'static,
128 Ext::UnrecoverableError: BackendSyscallError,
129 RunFallibleError: From<Ext::FallibleError>,
130 Ext::AllocError: BackendAllocSyscallError<ExtError = Ext::UnrecoverableError>,
131{
132 fn add_func(
133 &mut self,
134 name: SyscallName,
135 f: HostFuncType<HostState<Ext, BackendMemory<ExecutorMemory>>>,
136 ) {
137 self.env_def_builder.add_host_func("env", name.to_str(), f);
138
139 self.funcs_count += 1;
140 }
141
142 fn add_memory(&mut self, memory: BackendMemory<ExecutorMemory>) {
143 self.env_def_builder
144 .add_memory("env", "memory", memory.into_inner());
145 }
146}
147
148impl<Ext: BackendExternalities> From<EnvBuilder<Ext>>
149 for EnvironmentDefinitionBuilder<HostState<Ext, BackendMemory<ExecutorMemory>>>
150{
151 fn from(builder: EnvBuilder<Ext>) -> Self {
152 builder.env_def_builder
153 }
154}
155
156impl<Ext, EntryPoint> Environment<Ext, EntryPoint>
157where
158 Ext: BackendExternalities + Send + 'static,
159 Ext::UnrecoverableError: BackendSyscallError,
160 RunFallibleError: From<Ext::FallibleError>,
161 Ext::AllocError: BackendAllocSyscallError<ExtError = Ext::UnrecoverableError>,
162 EntryPoint: WasmEntryPoint,
163{
164 #[rustfmt::skip]
165 fn bind_funcs(builder: &mut EnvBuilder<Ext>) {
166 macro_rules! add_function {
167 ($syscall:ident, $func:ident) => {
168 builder.add_func($syscall, wrap_syscall!($func, $syscall));
169 };
170 }
171
172 add_function!(EnvVars, env_vars);
173 add_function!(BlockHeight, block_height);
174 add_function!(BlockTimestamp, block_timestamp);
175 add_function!(CreateProgram, create_program);
176 add_function!(CreateProgramWGas, create_program_wgas);
177 add_function!(Debug, debug);
178 add_function!(Panic, panic);
179 add_function!(OomPanic, oom_panic);
180 add_function!(Exit, exit);
181 add_function!(ReplyCode, reply_code);
182 add_function!(SignalCode, signal_code);
183 add_function!(ReserveGas, reserve_gas);
184 add_function!(ReplyDeposit, reply_deposit);
185 add_function!(UnreserveGas, unreserve_gas);
186 add_function!(GasAvailable, gas_available);
187 add_function!(Leave, leave);
188 add_function!(MessageId, message_id);
189 add_function!(ProgramId, program_id);
190 add_function!(Random, random);
191 add_function!(Read, read);
192 add_function!(Reply, reply);
193 add_function!(ReplyCommit, reply_commit);
194 add_function!(ReplyCommitWGas, reply_commit_wgas);
195 add_function!(ReplyPush, reply_push);
196 add_function!(ReplyTo, reply_to);
197 add_function!(SignalFrom, signal_from);
198 add_function!(ReplyWGas, reply_wgas);
199 add_function!(ReplyInput, reply_input);
200 add_function!(ReplyPushInput, reply_push_input);
201 add_function!(ReplyInputWGas, reply_input_wgas);
202 add_function!(Send, send);
203 add_function!(SendCommit, send_commit);
204 add_function!(SendCommitWGas, send_commit_wgas);
205 add_function!(SendInit, send_init);
206 add_function!(SendPush, send_push);
207 add_function!(SendWGas, send_wgas);
208 add_function!(SendInput, send_input);
209 add_function!(SendPushInput, send_push_input);
210 add_function!(SendInputWGas, send_input_wgas);
211 add_function!(Size, size);
212 add_function!(Source, source);
213 add_function!(Value, value);
214 add_function!(ValueAvailable, value_available);
215 add_function!(Wait, wait);
216 add_function!(WaitFor, wait_for);
217 add_function!(WaitUpTo, wait_up_to);
218 add_function!(Wake, wake);
219 add_function!(SystemReserveGas, system_reserve_gas);
220 add_function!(ReservationReply, reservation_reply);
221 add_function!(ReservationReplyCommit, reservation_reply_commit);
222 add_function!(ReservationSend, reservation_send);
223 add_function!(ReservationSendCommit, reservation_send_commit);
224 add_function!(SystemBreak, system_break);
225
226 add_function!(Alloc, alloc);
227 add_function!(Free, free);
228 add_function!(FreeRange, free_range);
229 }
230}
231
232#[cfg(feature = "std")]
233struct GlobalsAccessProvider<Ext: Externalities> {
234 instance: Instance<HostState<Ext, BackendMemory<ExecutorMemory>>>,
235 store: Option<Store<HostState<Ext, BackendMemory<ExecutorMemory>>>>,
236}
237
238#[cfg(feature = "std")]
239impl<Ext: Externalities + Send + 'static> GlobalsAccessor for GlobalsAccessProvider<Ext> {
240 fn get_i64(&mut self, name: &LimitedStr) -> Result<i64, GlobalsAccessError> {
241 let store = self.store.as_mut().ok_or(GlobalsAccessError)?;
242 self.instance
243 .get_global_val(store, name.as_str())
244 .and_then(i64::try_from_value)
245 .ok_or(GlobalsAccessError)
246 }
247
248 fn set_i64(&mut self, name: &LimitedStr, value: i64) -> Result<(), GlobalsAccessError> {
249 let store = self.store.as_mut().ok_or(GlobalsAccessError)?;
250 self.instance
251 .set_global_val(store, name.as_str(), Value::I64(value))
252 .map_err(|_| GlobalsAccessError)
253 }
254
255 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
256 self
257 }
258}
259
260impl<EnvExt, EntryPoint> Environment<EnvExt, EntryPoint>
261where
262 EnvExt: BackendExternalities + Send + 'static,
263 EnvExt::UnrecoverableError: BackendSyscallError,
264 RunFallibleError: From<EnvExt::FallibleError>,
265 EnvExt::AllocError: BackendAllocSyscallError<ExtError = EnvExt::UnrecoverableError>,
266 EntryPoint: WasmEntryPoint,
267{
268 pub fn new(
269 ext: EnvExt,
270 binary: &[u8],
271 entry_point: EntryPoint,
272 entries: BTreeSet<DispatchKind>,
273 mem_size: WasmPagesAmount,
274 ) -> Result<Self, EnvironmentError> {
275 use EnvironmentError::*;
276 use SystemEnvironmentError::*;
277
278 let mut store = Store::new(None);
279
280 let mut builder = EnvBuilder::<EnvExt> {
281 env_def_builder: EnvironmentDefinitionBuilder::new(),
282 funcs_count: 0,
283 };
284
285 let memory: BackendMemory<ExecutorMemory> =
286 match ExecutorMemory::new(&mut store, mem_size.into(), None) {
287 Ok(mem) => mem.into(),
288 Err(e) => return Err(System(CreateEnvMemory(e))),
289 };
290
291 builder.add_memory(memory.clone());
292
293 Self::bind_funcs(&mut builder);
294
295 assert_eq!(
299 builder.funcs_count,
300 SyscallName::all().count(),
301 "Not all existing syscalls were added to the module's env."
302 );
303
304 let env_builder: EnvironmentDefinitionBuilder<_> = builder.into();
305
306 *store.data_mut() = Some(State {
307 ext,
308 memory: memory.clone(),
309 termination_reason: ActorTerminationReason::Success.into(),
310 });
311
312 let instance = Instance::new(&mut store, binary, &env_builder).map_err(|e| {
313 Actor(
314 store_host_state_mut(&mut store).ext.gas_amount(),
315 format!("{e:?}"),
316 )
317 })?;
318
319 Ok(Self {
320 instance,
321 entries,
322 entry_point,
323 store,
324 memory,
325 })
326 }
327
328 pub fn execute(
329 self,
330 prepare_memory: impl FnOnce(
331 &mut Store<HostState<EnvExt, BackendMemory<ExecutorMemory>>>,
332 &mut BackendMemory<ExecutorMemory>,
333 GlobalsAccessConfig,
334 ),
335 ) -> EnvironmentExecutionResult<EnvExt> {
336 use EnvironmentError::*;
337 use SystemEnvironmentError::*;
338
339 let Self {
340 mut instance,
341 entries,
342 entry_point,
343 mut store,
344 mut memory,
345 } = self;
346
347 let gas = store_host_state_mut(&mut store)
348 .ext
349 .define_current_counter();
350
351 instance
352 .set_global_val(&mut store, GLOBAL_NAME_GAS, Value::I64(gas as i64))
353 .map_err(|_| System(WrongInjectedGas))?;
354
355 #[cfg(feature = "std")]
356 let mut globals_provider = GlobalsAccessProvider {
357 instance: instance.clone(),
358 store: None,
359 };
360 #[cfg(feature = "std")]
361 let globals_provider_dyn_ref = &mut globals_provider as &mut dyn GlobalsAccessor;
362
363 #[cfg(feature = "std")]
369 let globals_access_ptr = &globals_provider_dyn_ref as *const _ as HostPointer;
370
371 #[cfg(feature = "std")]
372 let globals_config = GlobalsAccessConfig {
373 access_ptr: globals_access_ptr,
374 access_mod: GlobalsAccessMod::NativeRuntime,
375 };
376
377 #[cfg(not(feature = "std"))]
378 let globals_config = GlobalsAccessConfig {
379 access_ptr: instance.get_instance_ptr(),
380 access_mod: GlobalsAccessMod::WasmRuntime,
381 };
382
383 prepare_memory(&mut store, &mut memory, globals_config);
384
385 let needs_execution = entry_point
386 .try_into_kind()
387 .map(|kind| entries.contains(&kind))
388 .unwrap_or(true);
389
390 let res = if needs_execution {
391 #[cfg(feature = "std")]
392 let res = {
393 let store_option = &mut globals_provider_dyn_ref
394 .as_any_mut()
395 .downcast_mut::<GlobalsAccessProvider<EnvExt>>()
396 .unwrap_or_else(|| {
398 let err_msg =
399 "Environment::execute: Provider must be `GlobalsAccessProvider`";
400
401 log::error!("{err_msg}");
402 unreachable!("{err_msg}")
403 })
404 .store;
405
406 store_option.replace(store);
407
408 let store_ref = store_option
409 .as_mut()
410 .unwrap_or_else(|| {
412 let err_msg = "Environment::execute: Store must be set before";
413
414 log::error!("{err_msg}");
415 unreachable!("{err_msg}")
416 });
417
418 let res = instance.invoke(store_ref, entry_point.as_entry(), &[]);
419
420 store = globals_provider.store.take().unwrap();
421
422 res
423 };
424
425 #[cfg(not(feature = "std"))]
426 let res = instance.invoke(&mut store, entry_point.as_entry(), &[]);
427
428 res
429 } else {
430 Ok(ReturnValue::Unit)
431 };
432
433 let gas = instance
435 .get_global_val(&mut store, GLOBAL_NAME_GAS)
436 .and_then(i64::try_from_value)
437 .ok_or(System(WrongInjectedGas))? as u64;
438
439 let state = store.data_mut().take().unwrap_or_else(|| {
440 let err_msg = "Environment::execute: State must be set";
441
442 log::error!("{err_msg}");
443 unreachable!("{err_msg}")
444 });
445
446 let (ext, termination_reason) = state.terminate(res, gas);
447
448 Ok(BackendReport {
449 termination_reason,
450 store,
451 memory,
452 ext,
453 })
454 }
455}