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 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 #[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 #[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 {
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}