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