1use crate::blueprints::package::*;
2use crate::errors::{ApplicationError, RuntimeError};
3use crate::internal_prelude::*;
4use crate::kernel::kernel_api::{KernelNodeApi, KernelSubstateApi};
5use crate::system::system_callback::*;
6use crate::system::system_callback_api::SystemCallbackObject;
7use crate::system::system_substates::KeyValueEntrySubstate;
8use crate::vm::wasm::{ScryptoV1WasmValidator, WasmEngine};
9use crate::vm::{NativeVm, NativeVmExtension, ScryptoVm};
10use radix_engine_interface::api::field_api::LockFlags;
11use radix_engine_interface::api::SystemApi;
12
13use crate::vm::ScryptoVmVersion;
14
15use super::wasm::DefaultWasmEngine;
16use super::NoExtension;
17
18pub type VmBootSubstate = VmBoot;
19
20#[derive(Debug, Clone, PartialEq, Eq, Sbor, ScryptoSborAssertion)]
21#[sbor_assert(backwards_compatible(cuttlefish = "FILE:vm_boot_substate_cuttlefish_schema.bin",))]
22pub enum VmBoot {
23 V1 { scrypto_version: u64 },
24}
25
26impl VmBoot {
27 pub fn load(substate_db: &impl SubstateDatabase) -> Self {
29 substate_db
30 .get_substate(
31 TRANSACTION_TRACKER,
32 BOOT_LOADER_PARTITION,
33 BootLoaderField::VmBoot,
34 )
35 .unwrap_or_else(|| Self::babylon_genesis())
36 }
37
38 pub fn latest() -> Self {
39 Self::bottlenose()
40 }
41
42 pub fn bottlenose() -> Self {
43 Self::V1 {
44 scrypto_version: ScryptoVmVersion::V1_1.into(),
45 }
46 }
47
48 pub fn babylon_genesis() -> Self {
49 Self::V1 {
50 scrypto_version: ScryptoVmVersion::V1_0.into(),
51 }
52 }
53}
54
55pub trait VmApi {
56 fn get_scrypto_version(&self) -> ScryptoVmVersion;
57}
58
59impl VmApi for VmBoot {
60 fn get_scrypto_version(&self) -> ScryptoVmVersion {
61 match self {
62 VmBoot::V1 { scrypto_version } => ScryptoVmVersion::try_from(*scrypto_version)
63 .expect(&format!("Unexpected scrypto version: {}", scrypto_version)),
64 }
65 }
66}
67
68pub trait VmInitialize {
76 type WasmEngine: WasmEngine;
77 type NativeVmExtension: NativeVmExtension;
78
79 fn get_vm_extension(&self) -> Self::NativeVmExtension;
80
81 fn get_scrypto_vm(&self) -> &ScryptoVm<Self::WasmEngine>;
82}
83
84pub struct VmModules<W: WasmEngine, E: NativeVmExtension> {
85 pub scrypto_vm: ScryptoVm<W>,
86 pub vm_extension: E,
87}
88
89impl<W: WasmEngine, E: NativeVmExtension> VmModules<W, E> {
90 pub fn new(scrypto_vm: ScryptoVm<W>, vm_extension: E) -> Self {
91 Self {
92 scrypto_vm,
93 vm_extension,
94 }
95 }
96}
97
98impl<E: NativeVmExtension> VmModules<DefaultWasmEngine, E> {
99 pub fn default_with_extension(vm_extension: E) -> Self {
100 Self {
101 scrypto_vm: Default::default(),
102 vm_extension,
103 }
104 }
105}
106
107pub type DefaultVmModules = VmModules<DefaultWasmEngine, NoExtension>;
108
109impl DefaultVmModules {
110 pub fn default() -> Self {
111 Self {
112 scrypto_vm: ScryptoVm::default(),
113 vm_extension: NoExtension,
114 }
115 }
116}
117
118impl<W: WasmEngine, E: NativeVmExtension> VmInitialize for VmModules<W, E> {
119 type WasmEngine = W;
120 type NativeVmExtension = E;
121
122 fn get_vm_extension(&self) -> Self::NativeVmExtension {
123 self.vm_extension.clone()
124 }
125
126 fn get_scrypto_vm(&self) -> &ScryptoVm<Self::WasmEngine> {
127 &self.scrypto_vm
128 }
129}
130
131pub struct VmInit<'g, W: WasmEngine, E: NativeVmExtension> {
132 pub scrypto_vm: &'g ScryptoVm<W>,
133 pub native_vm_extension: E,
134 pub vm_boot: VmBoot,
135}
136
137impl<'g, W: WasmEngine, E: NativeVmExtension> InitializationParameters for VmInit<'g, W, E> {
138 type For = Vm<'g, W, E>;
139}
140
141impl<'g, W: WasmEngine, E: NativeVmExtension> VmInit<'g, W, E> {
142 pub fn load(
143 substate_db: &impl SubstateDatabase,
144 vm_modules: &'g impl VmInitialize<WasmEngine = W, NativeVmExtension = E>,
145 ) -> Self {
146 Self {
147 scrypto_vm: vm_modules.get_scrypto_vm(),
148 native_vm_extension: vm_modules.get_vm_extension(),
149 vm_boot: VmBoot::load(substate_db),
150 }
151 }
152}
153
154pub struct Vm<'g, W: WasmEngine, E: NativeVmExtension> {
155 pub scrypto_vm: &'g ScryptoVm<W>,
156 pub native_vm: NativeVm<E>,
157 pub vm_boot: VmBoot,
158}
159
160impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<'g, W, E> {
161 type Init = VmInit<'g, W, E>;
162
163 fn init(vm_init: VmInit<'g, W, E>) -> Result<Self, BootloadingError> {
164 Ok(Self {
165 scrypto_vm: vm_init.scrypto_vm,
166 native_vm: NativeVm::new_with_extension(vm_init.native_vm_extension),
167 vm_boot: vm_init.vm_boot,
168 })
169 }
170
171 fn invoke<
172 Y: SystemApi<RuntimeError>
173 + SystemBasedKernelInternalApi<SystemCallback = Self>
174 + KernelNodeApi
175 + KernelSubstateApi<SystemLockData>,
176 >(
177 address: &PackageAddress,
178 export: PackageExport,
179 input: &IndexedScryptoValue,
180 api: &mut Y,
181 ) -> Result<IndexedScryptoValue, RuntimeError> {
182 let vm_type = {
183 let handle = api.kernel_open_substate_with_default(
184 address.as_node_id(),
185 MAIN_BASE_PARTITION
186 .at_offset(PACKAGE_VM_TYPE_PARTITION_OFFSET)
187 .unwrap(),
188 &SubstateKey::Map(scrypto_encode(&export.code_hash).unwrap()),
189 LockFlags::read_only(),
190 Some(|| {
191 let kv_entry = KeyValueEntrySubstate::<()>::default();
192 IndexedScryptoValue::from_typed(&kv_entry)
193 }),
194 SystemLockData::default(),
195 )?;
196 let vm_type = api.kernel_read_substate(handle)?;
197 let vm_type: PackageCodeVmTypeEntrySubstate = vm_type.as_typed().unwrap();
198 api.kernel_close_substate(handle)?;
199 vm_type
200 .into_value()
201 .unwrap_or_else(|| panic!("Vm type not found: {:?}", export))
202 };
203
204 let vm_api = api.kernel_get_system().callback.vm_boot.clone();
205
206 let output = match vm_type.fully_update_and_into_latest_version().vm_type {
207 VmType::Native => {
208 let original_code = {
209 let handle = api.kernel_open_substate_with_default(
210 address.as_node_id(),
211 MAIN_BASE_PARTITION
212 .at_offset(PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET)
213 .unwrap(),
214 &SubstateKey::Map(scrypto_encode(&export.code_hash).unwrap()),
215 LockFlags::read_only(),
216 Some(|| {
217 let kv_entry = KeyValueEntrySubstate::<()>::default();
218 IndexedScryptoValue::from_typed(&kv_entry)
219 }),
220 SystemLockData::default(),
221 )?;
222 let original_code = api.kernel_read_substate(handle)?;
223 let original_code: PackageCodeOriginalCodeEntrySubstate =
224 original_code.as_typed().unwrap();
225 api.kernel_close_substate(handle)?;
226 original_code
227 .into_value()
228 .unwrap_or_else(|| panic!("Original code not found: {:?}", export))
229 };
230
231 let mut vm_instance = api.kernel_get_system().callback.native_vm.create_instance(
232 address,
233 &original_code.fully_update_and_into_latest_version().code,
234 )?;
235 let output =
236 { vm_instance.invoke(export.export_name.as_str(), input, api, &vm_api)? };
237
238 output
239 }
240 VmType::ScryptoV1 => {
241 let instrumented_code = {
242 let handle = api.kernel_open_substate_with_default(
243 address.as_node_id(),
244 MAIN_BASE_PARTITION
245 .at_offset(PACKAGE_INSTRUMENTED_CODE_PARTITION_OFFSET)
246 .unwrap(),
247 &SubstateKey::Map(scrypto_encode(&export.code_hash).unwrap()),
248 LockFlags::read_only(),
249 Some(|| {
250 let kv_entry = KeyValueEntrySubstate::<()>::default();
251 IndexedScryptoValue::from_typed(&kv_entry)
252 }),
253 SystemLockData::default(),
254 )?;
255 let instrumented_code = api.kernel_read_substate(handle)?;
256 let instrumented_code: PackageCodeInstrumentedCodeEntrySubstate =
257 instrumented_code.as_typed().unwrap();
258 api.kernel_close_substate(handle)?;
259 instrumented_code
260 .into_value()
261 .unwrap_or_else(|| panic!("Instrumented code not found: {:?}", export))
262 .fully_update_and_into_latest_version()
263 };
264
265 let mut scrypto_vm_instance = {
266 api.kernel_get_system().callback.scrypto_vm.create_instance(
267 address,
268 export.code_hash,
269 &instrumented_code.instrumented_code,
270 )
271 };
272
273 api.consume_cost_units(ClientCostingEntry::PrepareWasmCode {
274 size: instrumented_code.instrumented_code.len(),
275 })?;
276
277 let output = {
278 scrypto_vm_instance.invoke(export.export_name.as_str(), input, api, &vm_api)?
279 };
280
281 output
282 }
283 };
284
285 Ok(output)
286 }
287}
288
289pub trait VmInvoke {
290 fn invoke<
292 Y: SystemApi<RuntimeError> + KernelNodeApi + KernelSubstateApi<SystemLockData>,
293 V: VmApi,
294 >(
295 &mut self,
296 export_name: &str,
297 input: &IndexedScryptoValue,
298 api: &mut Y,
299 vm_api: &V,
300 ) -> Result<IndexedScryptoValue, RuntimeError>;
301}
302
303pub struct VmPackageValidation;
304
305impl VmPackageValidation {
306 pub fn validate<V: VmApi>(
307 definition: &PackageDefinition,
308 vm_type: VmType,
309 code: &[u8],
310 vm_api: &V,
311 ) -> Result<Option<Vec<u8>>, RuntimeError> {
312 match vm_type {
313 VmType::Native => Ok(None),
314 VmType::ScryptoV1 => {
315 let version = vm_api.get_scrypto_version();
316
317 let instrumented_code = ScryptoV1WasmValidator::new(version)
319 .validate(&code, definition.blueprints.values())
320 .map_err(|e| {
321 RuntimeError::ApplicationError(ApplicationError::PackageError(
322 PackageError::InvalidWasm(e),
323 ))
324 })?
325 .0;
326
327 for BlueprintDefinitionInit {
328 is_transient,
329 blueprint_type,
330 feature_set,
331 schema:
332 BlueprintSchemaInit {
333 generics,
334 state:
335 BlueprintStateSchemaInit {
336 collections,
337 fields,
338 },
339 functions,
340 hooks,
341 ..
342 },
343 ..
344 } in definition.blueprints.values()
345 {
346 match blueprint_type {
347 BlueprintType::Outer => {}
348 BlueprintType::Inner { .. } => {
349 return Err(RuntimeError::ApplicationError(
350 ApplicationError::PackageError(PackageError::WasmUnsupported(
351 "Inner blueprints not supported".to_string(),
352 )),
353 ));
354 }
355 }
356
357 if !feature_set.is_empty() {
358 return Err(RuntimeError::ApplicationError(
359 ApplicationError::PackageError(PackageError::WasmUnsupported(
360 "Feature set not supported".to_string(),
361 )),
362 ));
363 }
364
365 if !collections.is_empty() {
366 return Err(RuntimeError::ApplicationError(
367 ApplicationError::PackageError(PackageError::WasmUnsupported(
368 "Static collections not supported".to_string(),
369 )),
370 ));
371 }
372
373 if fields.len() > 1 {
374 return Err(RuntimeError::ApplicationError(
375 ApplicationError::PackageError(PackageError::WasmUnsupported(
376 "More than 1 substate field not supported".to_string(),
377 )),
378 ));
379 }
380
381 for field in fields {
382 match &field.condition {
383 Condition::Always => {}
384 _ => {
385 return Err(RuntimeError::ApplicationError(
386 ApplicationError::PackageError(PackageError::WasmUnsupported(
387 "Conditional fields are not supported".to_string(),
388 )),
389 ));
390 }
391 }
392
393 match field.transience {
394 FieldTransience::NotTransient => {}
395 _ => {
396 return Err(RuntimeError::ApplicationError(
397 ApplicationError::PackageError(PackageError::WasmUnsupported(
398 "Transient fields are not supported".to_string(),
399 )),
400 ));
401 }
402 }
403 }
404
405 if !hooks.hooks.is_empty() {
406 return Err(RuntimeError::ApplicationError(
407 ApplicationError::PackageError(PackageError::WasmUnsupported(
408 "Hooks not supported".to_string(),
409 )),
410 ));
411 }
412
413 for (_name, schema) in &functions.functions {
414 if let Some(info) = &schema.receiver {
415 if info.ref_types != RefTypes::NORMAL {
416 return Err(RuntimeError::ApplicationError(
417 ApplicationError::PackageError(PackageError::WasmUnsupported(
418 "Irregular ref types not supported".to_string(),
419 )),
420 ));
421 }
422 }
423 }
424
425 if !generics.is_empty() {
426 return Err(RuntimeError::ApplicationError(
427 ApplicationError::PackageError(PackageError::WasmUnsupported(
428 "Generics not supported".to_string(),
429 )),
430 ));
431 }
432
433 if *is_transient {
434 return Err(RuntimeError::ApplicationError(
435 ApplicationError::PackageError(PackageError::WasmUnsupported(
436 "Transient blueprints not supported".to_string(),
437 )),
438 ));
439 }
440 }
441 Ok(Some(instrumented_code))
442 }
443 }
444 }
445}