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 .unwrap_or_else(|_| panic!("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 Default for DefaultVmModules {
110 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>
293 + KernelNodeApi
294 + KernelSubstateApi<SystemLockData>
295 + SystemBasedKernelInternalApi,
296 V: VmApi,
297 >(
298 &mut self,
299 export_name: &str,
300 input: &IndexedScryptoValue,
301 api: &mut Y,
302 vm_api: &V,
303 ) -> Result<IndexedScryptoValue, RuntimeError>;
304}
305
306pub struct VmPackageValidation;
307
308impl VmPackageValidation {
309 pub fn validate<V: VmApi>(
310 definition: &PackageDefinition,
311 vm_type: VmType,
312 code: &[u8],
313 vm_api: &V,
314 ) -> Result<Option<Vec<u8>>, RuntimeError> {
315 match vm_type {
316 VmType::Native => Ok(None),
317 VmType::ScryptoV1 => {
318 let version = vm_api.get_scrypto_version();
319
320 let instrumented_code = ScryptoV1WasmValidator::new(version)
322 .validate(code, definition.blueprints.values())
323 .map_err(|e| {
324 RuntimeError::ApplicationError(ApplicationError::PackageError(
325 PackageError::InvalidWasm(e),
326 ))
327 })?
328 .0;
329
330 for BlueprintDefinitionInit {
331 is_transient,
332 blueprint_type,
333 feature_set,
334 schema:
335 BlueprintSchemaInit {
336 generics,
337 state:
338 BlueprintStateSchemaInit {
339 collections,
340 fields,
341 },
342 functions,
343 hooks,
344 ..
345 },
346 ..
347 } in definition.blueprints.values()
348 {
349 match blueprint_type {
350 BlueprintType::Outer => {}
351 BlueprintType::Inner { .. } => {
352 return Err(RuntimeError::ApplicationError(
353 ApplicationError::PackageError(PackageError::WasmUnsupported(
354 "Inner blueprints not supported".to_string(),
355 )),
356 ));
357 }
358 }
359
360 if !feature_set.is_empty() {
361 return Err(RuntimeError::ApplicationError(
362 ApplicationError::PackageError(PackageError::WasmUnsupported(
363 "Feature set not supported".to_string(),
364 )),
365 ));
366 }
367
368 if !collections.is_empty() {
369 return Err(RuntimeError::ApplicationError(
370 ApplicationError::PackageError(PackageError::WasmUnsupported(
371 "Static collections not supported".to_string(),
372 )),
373 ));
374 }
375
376 if fields.len() > 1 {
377 return Err(RuntimeError::ApplicationError(
378 ApplicationError::PackageError(PackageError::WasmUnsupported(
379 "More than 1 substate field not supported".to_string(),
380 )),
381 ));
382 }
383
384 for field in fields {
385 match &field.condition {
386 Condition::Always => {}
387 _ => {
388 return Err(RuntimeError::ApplicationError(
389 ApplicationError::PackageError(PackageError::WasmUnsupported(
390 "Conditional fields are not supported".to_string(),
391 )),
392 ));
393 }
394 }
395
396 match field.transience {
397 FieldTransience::NotTransient => {}
398 _ => {
399 return Err(RuntimeError::ApplicationError(
400 ApplicationError::PackageError(PackageError::WasmUnsupported(
401 "Transient fields are not supported".to_string(),
402 )),
403 ));
404 }
405 }
406 }
407
408 if !hooks.hooks.is_empty() {
409 return Err(RuntimeError::ApplicationError(
410 ApplicationError::PackageError(PackageError::WasmUnsupported(
411 "Hooks not supported".to_string(),
412 )),
413 ));
414 }
415
416 for (_name, schema) in &functions.functions {
417 if let Some(info) = &schema.receiver {
418 if info.ref_types != RefTypes::NORMAL {
419 return Err(RuntimeError::ApplicationError(
420 ApplicationError::PackageError(PackageError::WasmUnsupported(
421 "Irregular ref types not supported".to_string(),
422 )),
423 ));
424 }
425 }
426 }
427
428 if !generics.is_empty() {
429 return Err(RuntimeError::ApplicationError(
430 ApplicationError::PackageError(PackageError::WasmUnsupported(
431 "Generics not supported".to_string(),
432 )),
433 ));
434 }
435
436 if *is_transient {
437 return Err(RuntimeError::ApplicationError(
438 ApplicationError::PackageError(PackageError::WasmUnsupported(
439 "Transient blueprints not supported".to_string(),
440 )),
441 ));
442 }
443 }
444 Ok(Some(instrumented_code))
445 }
446 }
447 }
448}