radix_engine/vm/
native_vm.rs

1use crate::blueprints::access_controller::v1::*;
2use crate::blueprints::access_controller::v2::*;
3use crate::blueprints::account::AccountBlueprintCuttlefishExtension;
4use crate::blueprints::account::{AccountBlueprintBottlenoseExtension, AccountNativePackage};
5use crate::blueprints::consensus_manager::{
6    ConsensusManagerNativePackage, ConsensusManagerSecondsPrecisionNativeCode,
7};
8use crate::blueprints::identity::IdentityNativePackage;
9use crate::blueprints::identity::IdentityV1MinorVersion;
10use crate::blueprints::locker::LockerNativePackage;
11use crate::blueprints::package::PackageNativePackage;
12use crate::blueprints::pool::v1::package::*;
13use crate::blueprints::resource::{ResourceNativePackage, WorktopBlueprintCuttlefishExtension};
14use crate::blueprints::test_utils::TestUtilsNativePackage;
15use crate::blueprints::transaction_processor::{
16    TransactionProcessorNativePackage, TransactionProcessorV1MinorVersion,
17};
18use crate::blueprints::transaction_tracker::TransactionTrackerNativePackage;
19use crate::errors::{NativeRuntimeError, RuntimeError, VmError};
20use crate::internal_prelude::*;
21use crate::kernel::kernel_api::{KernelNodeApi, KernelSubstateApi};
22use crate::object_modules::metadata::MetadataNativePackage;
23use crate::object_modules::role_assignment::*;
24use crate::object_modules::royalty::RoyaltyNativePackage;
25use crate::system::system_callback::SystemBasedKernelInternalApi;
26use crate::system::system_callback::SystemLockData;
27use crate::vm::{VmApi, VmInvoke};
28use radix_engine_interface::api::SystemApi;
29use radix_engine_interface::blueprints::package::*;
30use radix_engine_profiling_derive::trace_resources;
31
32#[derive(Clone)]
33pub struct NativeVm<E: NativeVmExtension> {
34    extension: E,
35}
36
37impl<E: NativeVmExtension> NativeVm<E> {
38    pub fn new_with_extension(extension: E) -> Self {
39        Self { extension }
40    }
41
42    pub fn create_instance(
43        &self,
44        package_address: &PackageAddress,
45        code: &[u8],
46    ) -> Result<NativeVmInstance<E::Instance>, RuntimeError> {
47        if let Some(custom_invoke) = self.extension.try_create_instance(code) {
48            return Ok(NativeVmInstance::Extension(custom_invoke));
49        }
50
51        let code: [u8; 8] = match code.try_into() {
52            Ok(code) => code,
53            // It should be impossible for us to get to this point here. The code argument is
54            // provided by the Vm after it reads the `PackageCodeOriginalCodeEntrySubstate`. Thus,
55            // if the code-id at this point is invalid for the native-vm, then this means that the
56            // database has been corrupted. We could safely panic here, however, we're choosing to
57            // keep the `Err` here for safety.
58            Err(..) => {
59                return Err(RuntimeError::VmError(VmError::Native(
60                    NativeRuntimeError::InvalidCodeId,
61                )));
62            }
63        };
64        let native_package_code_id = u64::from_be_bytes(code);
65        let instance = NativeVmInstance::Native {
66            package_address: *package_address,
67            native_package_code_id,
68        };
69
70        Ok(instance)
71    }
72}
73
74pub enum NativeVmInstance<I: VmInvoke> {
75    Native {
76        // Used by profiling
77        #[allow(dead_code)]
78        package_address: PackageAddress,
79        native_package_code_id: u64,
80    },
81    Extension(I),
82}
83
84impl<I: VmInvoke> NativeVmInstance<I> {
85    // Used by profiling
86    #[allow(dead_code)]
87    pub fn package_address(&self) -> PackageAddress {
88        match self {
89            NativeVmInstance::Native {
90                package_address, ..
91            } => *package_address,
92            _ => panic!("Profiling with NativeVmExtension is not supported."),
93        }
94    }
95}
96
97impl<I: VmInvoke> VmInvoke for NativeVmInstance<I> {
98    #[trace_resources(log=self.package_address().is_native_package(), log=self.package_address().to_hex(), log=export_name)]
99    fn invoke<
100        Y: SystemApi<RuntimeError>
101            + KernelNodeApi
102            + KernelSubstateApi<SystemLockData>
103            + SystemBasedKernelInternalApi,
104        V: VmApi,
105    >(
106        &mut self,
107        export_name: &str,
108        input: &IndexedScryptoValue,
109        api: &mut Y,
110        vm_api: &V,
111    ) -> Result<IndexedScryptoValue, RuntimeError> {
112        #[allow(unused_mut)]
113        let mut func = || match self {
114            NativeVmInstance::Extension(e) => e.invoke(export_name, input, api, vm_api),
115            NativeVmInstance::Native {
116                native_package_code_id,
117                package_address,
118            } => {
119                api.consume_cost_units(ClientCostingEntry::RunNativeCode {
120                    package_address,
121                    export_name,
122                    input_size: input.len(),
123                })?;
124
125                let code_id = NativeCodeId::from_repr(*native_package_code_id).ok_or(
126                    RuntimeError::VmError(VmError::Native(NativeRuntimeError::InvalidCodeId)),
127                )?;
128
129                match code_id {
130                    NativeCodeId::PackageCode1 => PackageNativePackage::invoke_export(
131                        export_name,
132                        input,
133                        PackageV1MinorVersion::Zero,
134                        api,
135                        vm_api,
136                    ),
137                    NativeCodeId::PackageCode2 => PackageNativePackage::invoke_export(
138                        export_name,
139                        input,
140                        PackageV1MinorVersion::One,
141                        api,
142                        vm_api,
143                    ),
144                    NativeCodeId::ResourceCode1 => {
145                        ResourceNativePackage::invoke_export(export_name, input, api)
146                    }
147                    NativeCodeId::ResourceCode2 => {
148                        WorktopBlueprintCuttlefishExtension::invoke_export(export_name, input, api)
149                    }
150                    NativeCodeId::ConsensusManagerCode1 => {
151                        ConsensusManagerNativePackage::invoke_export(export_name, input, api)
152                    }
153                    NativeCodeId::ConsensusManagerCode2 => {
154                        ConsensusManagerSecondsPrecisionNativeCode::invoke_export(
155                            export_name,
156                            input,
157                            api,
158                        )
159                    }
160                    NativeCodeId::IdentityCode1 => IdentityNativePackage::invoke_export(
161                        export_name,
162                        input,
163                        IdentityV1MinorVersion::Zero,
164                        api,
165                    ),
166                    NativeCodeId::IdentityCode2 => IdentityNativePackage::invoke_export(
167                        export_name,
168                        input,
169                        IdentityV1MinorVersion::One,
170                        api,
171                    ),
172                    NativeCodeId::AccountCode1 => {
173                        AccountNativePackage::invoke_export(export_name, input, api)
174                    }
175                    NativeCodeId::AccountCode2 => {
176                        AccountBlueprintBottlenoseExtension::invoke_export(export_name, input, api)
177                    }
178                    NativeCodeId::AccountCode3 => {
179                        AccountBlueprintCuttlefishExtension::invoke_export(export_name, input, api)
180                    }
181                    NativeCodeId::AccessControllerCode1 => {
182                        AccessControllerV1NativePackage::invoke_export(export_name, input, api)
183                    }
184                    NativeCodeId::AccessControllerCode2 => {
185                        AccessControllerV2NativePackage::invoke_export(export_name, input, api)
186                    }
187                    NativeCodeId::TransactionProcessorCode1 => {
188                        TransactionProcessorNativePackage::invoke_export(
189                            export_name,
190                            input,
191                            TransactionProcessorV1MinorVersion::Zero,
192                            api,
193                        )
194                    }
195                    NativeCodeId::TransactionProcessorCode2 => {
196                        TransactionProcessorNativePackage::invoke_export(
197                            export_name,
198                            input,
199                            TransactionProcessorV1MinorVersion::One,
200                            api,
201                        )
202                    }
203                    NativeCodeId::MetadataCode1 => {
204                        MetadataNativePackage::invoke_export(export_name, input, api)
205                    }
206                    NativeCodeId::RoyaltyCode1 => {
207                        RoyaltyNativePackage::invoke_export(export_name, input, api)
208                    }
209                    NativeCodeId::RoleAssignmentCode1 => {
210                        RoleAssignmentNativePackage::invoke_export(export_name, input, api)
211                    }
212                    NativeCodeId::RoleAssignmentCode2 => {
213                        RoleAssignmentBottlenoseExtension::invoke_export(export_name, input, api)
214                    }
215                    NativeCodeId::PoolCode1 => PoolNativePackage::invoke_export(
216                        export_name,
217                        input,
218                        PoolV1MinorVersion::Zero,
219                        api,
220                    ),
221                    NativeCodeId::PoolCode2 => PoolNativePackage::invoke_export(
222                        export_name,
223                        input,
224                        PoolV1MinorVersion::One,
225                        api,
226                    ),
227                    NativeCodeId::TransactionTrackerCode1 => {
228                        TransactionTrackerNativePackage::invoke_export(export_name, input, api)
229                    }
230                    NativeCodeId::TestUtilsCode1 => {
231                        TestUtilsNativePackage::invoke_export(export_name, input, api)
232                    }
233                    NativeCodeId::LockerCode1 => {
234                        LockerNativePackage::invoke_export(export_name, input, api)
235                    }
236                }
237            }
238        };
239
240        // Note: we can't unwind if we're compiling for no-std. See:
241        // https://github.com/rust-lang/rfcs/issues/2810
242        {
243            #[cfg(feature = "std")]
244            {
245                match std::panic::catch_unwind(std::panic::AssertUnwindSafe(func)) {
246                    Ok(rtn) => rtn,
247                    Err(cause) => {
248                        let message = if let Some(s) = cause.downcast_ref::<&'static str>() {
249                            (*s).to_string()
250                        } else if let Some(s) = cause.downcast_ref::<String>() {
251                            s.clone()
252                        } else {
253                            "Unknown panic!".to_string()
254                        };
255                        Err(RuntimeError::VmError(VmError::Native(
256                            NativeRuntimeError::Trap {
257                                export_name: export_name.to_owned(),
258                                input: input.as_scrypto_value().clone(),
259                                error: message,
260                            },
261                        )))
262                    }
263                }
264            }
265
266            #[cfg(not(feature = "std"))]
267            func()
268        }
269    }
270}
271
272pub trait NativeVmExtension: Clone {
273    type Instance: VmInvoke + Clone;
274
275    fn try_create_instance(&self, code: &[u8]) -> Option<Self::Instance>;
276}
277
278#[derive(Clone)]
279pub struct NoExtension;
280impl NativeVmExtension for NoExtension {
281    type Instance = NullVmInvoke;
282    fn try_create_instance(&self, _code: &[u8]) -> Option<Self::Instance> {
283        None
284    }
285}
286
287pub type DefaultNativeVm = NativeVm<NoExtension>;
288
289impl DefaultNativeVm {
290    pub fn new() -> Self {
291        NativeVm::new_with_extension(NoExtension)
292    }
293}
294
295impl Default for DefaultNativeVm {
296    fn default() -> Self {
297        Self::new()
298    }
299}
300
301#[derive(Clone)]
302pub struct NullVmInvoke;
303
304impl VmInvoke for NullVmInvoke {
305    fn invoke<
306        Y: SystemApi<RuntimeError> + KernelNodeApi + KernelSubstateApi<SystemLockData>,
307        V: VmApi,
308    >(
309        &mut self,
310        _export_name: &str,
311        _input: &IndexedScryptoValue,
312        _api: &mut Y,
313        _vm_api: &V,
314    ) -> Result<IndexedScryptoValue, RuntimeError> {
315        panic!("Invocation was called on null VmInvoke");
316    }
317}
318
319#[derive(Clone)]
320pub struct OverridePackageCode<C: VmInvoke + Clone> {
321    custom_package_code_id: u64,
322    custom_invoke: C,
323}
324
325impl<C: VmInvoke + Clone> OverridePackageCode<C> {
326    pub fn new(custom_package_code_id: u64, custom_invoke: C) -> Self {
327        Self {
328            custom_package_code_id,
329            custom_invoke,
330        }
331    }
332}
333
334impl<C: VmInvoke + Clone> NativeVmExtension for OverridePackageCode<C> {
335    type Instance = C;
336
337    fn try_create_instance(&self, code: &[u8]) -> Option<C> {
338        let code_id = {
339            let code: [u8; 8] = match code.try_into() {
340                Ok(code) => code,
341                Err(..) => return None,
342            };
343            u64::from_be_bytes(code)
344        };
345
346        if self.custom_package_code_id == code_id {
347            Some(self.custom_invoke.clone())
348        } else {
349            None
350        }
351    }
352}