radix_engine/system/
system_callback.rs

1use super::module::*;
2use super::system_modules::costing::{CostingModuleConfig, ExecutionCostingEntry};
3use super::type_info::{TypeInfoBlueprint, TypeInfoSubstate};
4use crate::blueprints::account::ACCOUNT_CREATE_PREALLOCATED_ED25519_ID;
5use crate::blueprints::account::ACCOUNT_CREATE_PREALLOCATED_SECP256K1_ID;
6use crate::blueprints::consensus_manager::*;
7use crate::blueprints::identity::IDENTITY_CREATE_PREALLOCATED_ED25519_ID;
8use crate::blueprints::identity::IDENTITY_CREATE_PREALLOCATED_SECP256K1_ID;
9use crate::blueprints::resource::fungible_vault::{DepositEvent, PayFeeEvent};
10use crate::blueprints::resource::*;
11use crate::blueprints::transaction_tracker::*;
12use crate::errors::*;
13use crate::internal_prelude::*;
14use crate::kernel::call_frame::{CallFrameInit, CallFrameMessage, StableReferenceType};
15use crate::kernel::kernel_api::*;
16use crate::kernel::kernel_callback_api::*;
17use crate::system::actor::Actor;
18use crate::system::actor::BlueprintHookActor;
19use crate::system::actor::FunctionActor;
20use crate::system::actor::MethodActor;
21use crate::system::module::InitSystemModule;
22use crate::system::system::SystemService;
23use crate::system::system_callback_api::SystemCallbackObject;
24use crate::system::system_db_reader::SystemDatabaseReader;
25use crate::system::system_modules::auth::AuthModule;
26use crate::system::system_modules::costing::*;
27use crate::system::system_modules::execution_trace::ExecutionTraceModule;
28use crate::system::system_modules::kernel_trace::KernelTraceModule;
29use crate::system::system_modules::limits::LimitsModule;
30use crate::system::system_modules::transaction_runtime::TransactionRuntimeModule;
31use crate::system::system_modules::{EnabledModules, SystemModuleMixer};
32use crate::system::system_substates::KeyValueEntrySubstate;
33use crate::system::system_type_checker::{BlueprintTypeTarget, KVStoreTypeTarget};
34use crate::system::transaction::multithread_intent_processor::MultiThreadIntentProcessor;
35use crate::track::*;
36use crate::transaction::*;
37use radix_blueprint_schema_init::RefTypes;
38use radix_engine_interface::api::field_api::LockFlags;
39use radix_engine_interface::api::SystemObjectApi;
40use radix_engine_interface::api::{CollectionIndex, SystemBlueprintApi};
41use radix_engine_interface::blueprints::account::ACCOUNT_BLUEPRINT;
42use radix_engine_interface::blueprints::hooks::*;
43use radix_engine_interface::blueprints::identity::IDENTITY_BLUEPRINT;
44use radix_engine_interface::blueprints::package::*;
45use radix_engine_interface::blueprints::transaction_processor::*;
46use radix_substate_store_interface::interface::*;
47use radix_transactions::model::*;
48
49#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
50pub struct SystemParameters {
51    pub network_definition: NetworkDefinition,
52    pub costing_module_config: CostingModuleConfig,
53    pub costing_parameters: CostingParameters,
54    pub limit_parameters: LimitParameters,
55}
56
57impl SystemParameters {
58    pub fn latest(network_definition: NetworkDefinition) -> Self {
59        Self::bottlenose(network_definition)
60    }
61
62    pub fn bottlenose(network_definition: NetworkDefinition) -> Self {
63        Self {
64            network_definition,
65            costing_module_config: CostingModuleConfig::bottlenose(),
66            costing_parameters: CostingParameters::babylon_genesis(),
67            limit_parameters: LimitParameters::babylon_genesis(),
68        }
69    }
70
71    pub fn babylon_genesis(network_definition: NetworkDefinition) -> Self {
72        Self {
73            network_definition,
74            costing_module_config: CostingModuleConfig::babylon_genesis(),
75            costing_parameters: CostingParameters::babylon_genesis(),
76            limit_parameters: LimitParameters::babylon_genesis(),
77        }
78    }
79}
80
81pub type SystemBootSubstate = SystemBoot;
82
83#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ScryptoSborAssertion)]
84#[sbor_assert(backwards_compatible(
85    cuttlefish = "FILE:system_boot_substate_cuttlefish_schema.bin",
86    dugong = "FILE:system_boot_substate_dugong_schema.bin",
87))]
88pub enum SystemBoot {
89    V1(SystemParameters),
90    V2(SystemVersion, SystemParameters),
91}
92
93impl SystemBoot {
94    /// Loads system boot from the database, or resolves a fallback..
95    ///
96    /// # Panics
97    /// This method panics if the database is pre-bottlenose and the execution config
98    /// does not specify a network definition.
99    pub fn load(substate_db: &impl SubstateDatabase, execution_config: &ExecutionConfig) -> Self {
100        substate_db
101            .get_substate(
102                TRANSACTION_TRACKER,
103                BOOT_LOADER_PARTITION,
104                BootLoaderField::SystemBoot,
105            )
106            .unwrap_or_else(|| {
107                let overrides = execution_config.system_overrides.as_ref();
108                let network_definition = overrides.and_then(|o| o.network_definition.as_ref())
109                    .expect("Before bottlenose, no SystemBoot substate exists, so a network_definition must be provided in the SystemOverrides of the ExecutionConfig.");
110                SystemBoot::babylon_genesis(network_definition.clone())
111            })
112    }
113
114    pub fn latest(network_definition: NetworkDefinition) -> Self {
115        Self::cuttlefish(network_definition)
116    }
117
118    pub fn cuttlefish(network_definition: NetworkDefinition) -> Self {
119        SystemBoot::V2(
120            SystemVersion::V3,
121            SystemParameters::bottlenose(network_definition),
122        )
123    }
124
125    pub fn cuttlefish_part1_for_previous_parameters(parameters: SystemParameters) -> Self {
126        SystemBoot::V2(SystemVersion::V2, parameters)
127    }
128
129    pub fn cuttlefish_part2_for_previous_parameters(parameters: SystemParameters) -> Self {
130        SystemBoot::V2(SystemVersion::V3, parameters)
131    }
132
133    pub fn dugong_for_previous_parameters(parameters: SystemParameters) -> Self {
134        SystemBoot::V2(SystemVersion::V4, parameters)
135    }
136
137    pub fn bottlenose(network_definition: NetworkDefinition) -> Self {
138        SystemBoot::V1(SystemParameters::bottlenose(network_definition))
139    }
140
141    pub fn babylon_genesis(network_definition: NetworkDefinition) -> Self {
142        SystemBoot::V1(SystemParameters::babylon_genesis(network_definition))
143    }
144
145    pub fn system_version(&self) -> SystemVersion {
146        match self {
147            Self::V1(..) => SystemVersion::V1,
148            Self::V2(version, _) => *version,
149        }
150    }
151
152    pub fn into_parameters(self) -> SystemParameters {
153        match self {
154            Self::V1(parameters) => parameters,
155            Self::V2(_, parameters) => parameters,
156        }
157    }
158}
159
160/// System logic which may change given a protocol version
161#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ScryptoSbor)]
162pub enum SystemVersion {
163    V1,
164    V2,
165    V3,
166    V4,
167}
168
169impl SystemVersion {
170    pub const fn latest() -> Self {
171        Self::V4
172    }
173
174    fn create_auth_module(
175        self,
176        executable: &ExecutableTransaction,
177    ) -> Result<AuthModule, RejectionReason> {
178        let auth_module = if self <= SystemVersion::V1 {
179            // This isn't exactly a necessary check as node logic should protect against this
180            // but keep it here for sanity
181            if !executable.subintents().is_empty() {
182                return Err(RejectionReason::SubintentsNotYetSupported);
183            }
184            let intent = executable.transaction_intent();
185            AuthModule::new_with_transaction_processor_auth_zone(intent.auth_zone_init.clone())
186        } else {
187            AuthModule::new()
188        };
189
190        Ok(auth_module)
191    }
192
193    fn execute_transaction<Y: SystemBasedKernelApi>(
194        self,
195        api: &mut Y,
196        executable: &ExecutableTransaction,
197        global_address_reservations: Vec<GlobalAddressReservation>,
198    ) -> Result<Vec<InstructionOutput>, RuntimeError> {
199        let output = if self <= SystemVersion::V1 {
200            let mut system_service = SystemService::new(api);
201            let intent = executable.transaction_intent();
202            let rtn = system_service.call_function(
203                TRANSACTION_PROCESSOR_PACKAGE,
204                TRANSACTION_PROCESSOR_BLUEPRINT,
205                TRANSACTION_PROCESSOR_RUN_IDENT,
206                scrypto_encode(&TransactionProcessorRunInputEfficientEncodable {
207                    manifest_encoded_instructions: intent.encoded_instructions.as_ref(),
208                    global_address_reservations: global_address_reservations.as_slice(),
209                    references: &intent.references,
210                    blobs: &intent.blobs,
211                })
212                .unwrap(),
213            )?;
214            let output: Vec<InstructionOutput> = scrypto_decode(&rtn).unwrap();
215            output
216        } else {
217            let mut txn_threads = MultiThreadIntentProcessor::init(
218                executable,
219                global_address_reservations.as_slice(),
220                api,
221            )?;
222            txn_threads.execute(api)?;
223            let output = txn_threads
224                .threads
225                .get_mut(0)
226                .unwrap()
227                .0
228                .outputs
229                .drain(..)
230                .collect();
231            output
232        };
233
234        Ok(output)
235    }
236
237    pub fn should_consume_cost_units<Y: SystemBasedKernelApi>(self, api: &mut Y) -> bool {
238        if self <= SystemVersion::V1 {
239            // Skip client-side costing requested by TransactionProcessor
240            api.kernel_get_current_stack_depth_uncosted() != 1
241        } else {
242            true
243        }
244    }
245
246    pub fn should_inject_transaction_processor_proofs_in_call_function(self) -> bool {
247        self <= SystemVersion::V1
248    }
249
250    pub fn should_charge_for_transaction_intent(self) -> bool {
251        self >= SystemVersion::V2
252    }
253
254    pub fn use_root_for_verify_parent_instruction(self) -> bool {
255        self <= SystemVersion::V2
256    }
257
258    pub fn assert_access_rule_is_noop_when_auth_module_disabled(self) -> bool {
259        self >= SystemVersion::V4
260    }
261}
262
263#[derive(Clone, Default)]
264pub enum SystemLockData {
265    KeyValueEntry(KeyValueEntryLockData),
266    Field(FieldLockData),
267    #[default]
268    Default,
269}
270
271#[derive(Clone)]
272pub enum KeyValueEntryLockData {
273    Read,
274    KVStoreWrite {
275        kv_store_validation_target: KVStoreTypeTarget,
276    },
277    KVCollectionWrite {
278        target: BlueprintTypeTarget,
279        collection_index: CollectionIndex,
280    },
281}
282
283#[derive(Clone)]
284#[allow(clippy::large_enum_variant)]
285pub enum FieldLockData {
286    Read,
287    Write {
288        target: BlueprintTypeTarget,
289        field_index: u8,
290    },
291}
292
293impl SystemLockData {
294    pub fn is_kv_entry(&self) -> bool {
295        matches!(self, SystemLockData::KeyValueEntry(..))
296    }
297
298    pub fn is_kv_entry_with_write(&self) -> bool {
299        matches!(
300            self,
301            SystemLockData::KeyValueEntry(KeyValueEntryLockData::KVCollectionWrite { .. })
302                | SystemLockData::KeyValueEntry(KeyValueEntryLockData::KVStoreWrite { .. })
303        )
304    }
305}
306
307/// Effectively a trait alias for `KernelApi<CallbackObject = System<Self::SystemCallback, Self::Executable>>`
308pub trait SystemBasedKernelApi: KernelApi<CallbackObject = System<Self::SystemCallback>> {
309    type SystemCallback: SystemCallbackObject;
310
311    fn system_service(&mut self) -> SystemService<'_, Self> {
312        SystemService::new(self)
313    }
314}
315
316impl<V: SystemCallbackObject, K: KernelApi<CallbackObject = System<V>>> SystemBasedKernelApi for K {
317    type SystemCallback = V;
318}
319
320/// Effectively a trait alias for `KernelInternalApi<CallbackObject = System<Self::SystemCallback, Self::Executable>>`
321pub trait SystemBasedKernelInternalApi:
322    KernelInternalApi<System = System<Self::SystemCallback>>
323{
324    type SystemCallback: SystemCallbackObject;
325
326    fn system_module_api(&mut self) -> SystemModuleApiImpl<'_, Self> {
327        SystemModuleApiImpl::new(self)
328    }
329}
330
331impl<V: SystemCallbackObject, K: KernelInternalApi<System = System<V>>> SystemBasedKernelInternalApi
332    for K
333{
334    type SystemCallback = V;
335}
336
337pub struct SystemInit<I: InitializationParameters<For: SystemCallbackObject<Init = I>>> {
338    pub self_init: SystemSelfInit,
339    pub callback_init: I,
340}
341
342impl<I: InitializationParameters<For: SystemCallbackObject<Init = I>>> SystemInit<I> {
343    /// This is expected to follow up with a match statement and a call to `v1` / `v2`
344    /// etc, to select the correct concrete generic.
345    pub fn load(
346        substate_db: &impl SubstateDatabase,
347        execution_config: ExecutionConfig,
348        callback_init: I,
349    ) -> Self {
350        let system_boot = SystemBoot::load(substate_db, &execution_config);
351        let self_init = SystemSelfInit::new(
352            execution_config,
353            system_boot.system_version(),
354            system_boot.into_parameters(),
355        );
356        Self {
357            self_init,
358            callback_init,
359        }
360    }
361}
362
363impl<I: InitializationParameters<For: SystemCallbackObject<Init = I>>> InitializationParameters
364    for SystemInit<I>
365{
366    type For = System<I::For>;
367}
368
369pub struct SystemSelfInit {
370    // These fields only affect side effects and do not affect ledger state execution
371    pub enable_kernel_trace: bool,
372    pub enable_cost_breakdown: bool,
373    pub execution_trace: Option<usize>,
374    pub enable_debug_information: bool,
375
376    // Configuration
377    pub system_parameters: SystemParameters,
378    pub system_logic_version: SystemVersion,
379    pub system_overrides: Option<SystemOverrides>,
380}
381
382impl SystemSelfInit {
383    pub fn new(
384        execution_config: ExecutionConfig,
385        system_logic_version: SystemVersion,
386        system_parameters: SystemParameters,
387    ) -> Self {
388        Self {
389            enable_kernel_trace: execution_config.enable_kernel_trace,
390            enable_cost_breakdown: execution_config.enable_cost_breakdown,
391            enable_debug_information: execution_config.enable_debug_information,
392            execution_trace: execution_config.execution_trace,
393            system_overrides: execution_config.system_overrides,
394            system_logic_version,
395            system_parameters,
396        }
397    }
398}
399
400pub struct System<V: SystemCallbackObject> {
401    pub versioned_system_logic: SystemVersion,
402    pub callback: V,
403    pub blueprint_cache: NonIterMap<CanonicalBlueprintId, Rc<BlueprintDefinition>>,
404    pub schema_cache: NonIterMap<SchemaHash, Rc<VersionedScryptoSchema>>,
405    pub auth_cache: NonIterMap<CanonicalBlueprintId, AuthConfig>,
406    pub modules: SystemModuleMixer,
407    pub finalization: SystemFinalization,
408}
409
410pub trait HasModules {
411    fn modules_mut(&mut self) -> &mut SystemModuleMixer;
412}
413
414impl<V: SystemCallbackObject> HasModules for System<V> {
415    #[inline]
416    fn modules_mut(&mut self) -> &mut SystemModuleMixer {
417        &mut self.modules
418    }
419}
420
421pub struct SystemFinalization {
422    pub intent_nullifications: Vec<IntentHashNullification>,
423}
424
425impl SystemFinalization {
426    pub fn no_nullifications() -> Self {
427        Self {
428            intent_nullifications: vec![],
429        }
430    }
431}
432
433impl<V: SystemCallbackObject> System<V> {
434    pub fn new(
435        versioned_system_logic: SystemVersion,
436        callback: V,
437        modules: SystemModuleMixer,
438        finalization: SystemFinalization,
439    ) -> Self {
440        Self {
441            callback,
442            blueprint_cache: NonIterMap::new(),
443            auth_cache: NonIterMap::new(),
444            schema_cache: NonIterMap::new(),
445            modules,
446            finalization,
447            versioned_system_logic,
448        }
449    }
450
451    fn on_move_node<Y: SystemBasedKernelApi>(
452        node_id: &NodeId,
453        is_moving_down: bool,
454        is_to_barrier: bool,
455        destination_blueprint_id: Option<BlueprintId>,
456        api: &mut Y,
457    ) -> Result<(), RuntimeError> {
458        let type_info = TypeInfoBlueprint::get_type(node_id, api)?;
459
460        match type_info {
461            TypeInfoSubstate::Object(object_info) => {
462                let mut service = SystemService::new(api);
463                let definition = service.load_blueprint_definition(
464                    object_info.blueprint_info.blueprint_id.package_address,
465                    &BlueprintVersionKey {
466                        blueprint: object_info
467                            .blueprint_info
468                            .blueprint_id
469                            .blueprint_name
470                            .clone(),
471                        version: BlueprintVersion::default(),
472                    },
473                )?;
474                if definition.hook_exports.contains_key(&BlueprintHook::OnMove) {
475                    api.kernel_invoke(Box::new(KernelInvocation {
476                        call_frame_data: Actor::BlueprintHook(BlueprintHookActor {
477                            receiver: Some(*node_id),
478                            blueprint_id: object_info.blueprint_info.blueprint_id.clone(),
479                            hook: BlueprintHook::OnMove,
480                        }),
481                        args: IndexedScryptoValue::from_typed(&OnMoveInput {
482                            is_moving_down,
483                            is_to_barrier,
484                            destination_blueprint_id,
485                        }),
486                    }))
487                    .map(|_| ())
488                } else {
489                    Ok(())
490                }
491            }
492            TypeInfoSubstate::KeyValueStore(_)
493            | TypeInfoSubstate::GlobalAddressReservation(_)
494            | TypeInfoSubstate::GlobalAddressPhantom(_) => Ok(()),
495        }
496    }
497}
498
499impl<V: SystemCallbackObject> System<V> {
500    #[cfg(not(feature = "alloc"))]
501    fn print_executable(executable: &ExecutableTransaction) {
502        println!("{:-^120}", "Executable");
503        println!("Intent hash: {}", executable.unique_hash());
504        println!("Payload size: {}", executable.payload_size());
505        println!(
506            "Transaction costing parameters: {:?}",
507            executable.costing_parameters()
508        );
509        println!(
510            "Pre-allocated addresses: {:?}",
511            executable.pre_allocated_addresses()
512        );
513        println!("Blobs: {:?}", executable.all_blob_hashes());
514        println!("References: {:?}", executable.all_references());
515    }
516
517    fn read_epoch_uncosted<S: CommitableSubstateStore>(store: &mut S) -> Option<Epoch> {
518        // TODO - Instead of doing a check of the exact epoch, we could do a check in range [X, Y]
519        //        Which could allow for better caching of transaction validity over epoch boundaries
520        match store.read_substate(
521            CONSENSUS_MANAGER.as_node_id(),
522            MAIN_BASE_PARTITION,
523            &ConsensusManagerField::State.into(),
524        ) {
525            Some(x) => {
526                let substate: FieldSubstate<ConsensusManagerStateFieldPayload> =
527                    x.as_typed().unwrap();
528                Some(substate.into_payload().into_unique_version().epoch)
529            }
530            None => None,
531        }
532    }
533
534    fn validate_epoch_range(
535        current_epoch: Epoch,
536        start_epoch_inclusive: Epoch,
537        end_epoch_exclusive: Epoch,
538    ) -> Result<(), RejectionReason> {
539        if current_epoch < start_epoch_inclusive {
540            return Err(RejectionReason::TransactionEpochNotYetValid {
541                valid_from: start_epoch_inclusive,
542                current_epoch,
543            });
544        }
545        if current_epoch >= end_epoch_exclusive {
546            return Err(RejectionReason::TransactionEpochNoLongerValid {
547                valid_until: end_epoch_exclusive.previous().unwrap_or(Epoch::zero()),
548                current_epoch,
549            });
550        }
551
552        Ok(())
553    }
554
555    fn validate_intent_hash_uncosted<S: CommitableSubstateStore>(
556        store: &mut S,
557        intent_hash: IntentHash,
558        expiry_epoch: Epoch,
559    ) -> Result<(), RejectionReason> {
560        let substate: FieldSubstate<TransactionTrackerSubstate> = store
561            .read_substate(
562                TRANSACTION_TRACKER.as_node_id(),
563                MAIN_BASE_PARTITION,
564                &TransactionTrackerField::TransactionTracker.into(),
565            )
566            .unwrap()
567            .as_typed()
568            .unwrap();
569
570        let partition_number = substate
571            .into_payload()
572            .v1()
573            .partition_for_expiry_epoch(expiry_epoch)
574            .expect("Transaction tracker should cover all valid epoch ranges");
575
576        let substate = store.read_substate(
577            TRANSACTION_TRACKER.as_node_id(),
578            PartitionNumber(partition_number),
579            &SubstateKey::Map(scrypto_encode(intent_hash.as_hash()).unwrap()),
580        );
581
582        if let Some(value) = substate {
583            let substate: KeyValueEntrySubstate<TransactionStatus> = value.as_typed().unwrap();
584            if let Some(status) = substate.into_value() {
585                match status.into_v1() {
586                    TransactionStatusV1::CommittedSuccess
587                    | TransactionStatusV1::CommittedFailure => {
588                        return Err(RejectionReason::IntentHashPreviouslyCommitted(intent_hash));
589                    }
590                    TransactionStatusV1::Cancelled => {
591                        return Err(RejectionReason::IntentHashPreviouslyCancelled(intent_hash));
592                    }
593                }
594            }
595        }
596
597        Ok(())
598    }
599
600    fn determine_result_type(
601        interpretation_result: Result<Vec<InstructionOutput>, TransactionExecutionError>,
602        fee_reserve: &mut SystemLoanFeeReserve,
603    ) -> TransactionResultType {
604        // A `SuccessButFeeLoanNotRepaid` error is issued if a transaction finishes before
605        // the SYSTEM_LOAN_AMOUNT is reached (which trigger a repay event) and even though
606        // enough fee has been locked.
607        //
608        // Do another `repay` try during finalization to remedy it.
609        let final_repay_result = fee_reserve.repay_all();
610
611        match interpretation_result {
612            Ok(output) => match final_repay_result {
613                Ok(_) => TransactionResultType::Commit(Ok(output)), // success and system loan repaid fully
614                Err(e) => {
615                    if let Some(abort_reason) = e.abortion() {
616                        TransactionResultType::Abort(abort_reason.clone())
617                    } else {
618                        TransactionResultType::Reject(RejectionReason::SuccessButFeeLoanNotRepaid)
619                    }
620                }
621            },
622            Err(e) => match e {
623                TransactionExecutionError::BootloadingError(e) => {
624                    TransactionResultType::Reject(RejectionReason::BootloadingError(e))
625                }
626                TransactionExecutionError::RuntimeError(e) => {
627                    if let Some(abort_reason) = e.abortion() {
628                        TransactionResultType::Abort(abort_reason.clone())
629                    } else if fee_reserve.fully_repaid() {
630                        TransactionResultType::Commit(Err(e))
631                    } else {
632                        TransactionResultType::Reject(
633                            RejectionReason::ErrorBeforeLoanAndDeferredCostsRepaid(e),
634                        )
635                    }
636                }
637            },
638        }
639    }
640
641    #[allow(clippy::type_complexity)]
642    fn finalize_fees_for_commit<S: SubstateDatabase>(
643        track: &mut Track<S>,
644        fee_reserve: SystemLoanFeeReserve,
645        is_success: bool,
646    ) -> (
647        FeeReserveFinalizationSummary,
648        IndexMap<NodeId, Decimal>,
649        Vec<(EventTypeIdentifier, Vec<u8>)>,
650        CostingParameters,
651        TransactionCostingParameters,
652    ) {
653        let mut events = Vec::<(EventTypeIdentifier, Vec<u8>)>::new();
654
655        // Distribute royalty
656        for (recipient, amount) in fee_reserve.royalty_cost_breakdown().clone() {
657            let node_id = recipient.vault_id();
658            let substate_key = FungibleVaultField::Balance.into();
659            let mut vault_balance = track
660                .read_substate(&node_id, MAIN_BASE_PARTITION, &substate_key)
661                .unwrap()
662                .as_typed::<FungibleVaultBalanceFieldSubstate>()
663                .unwrap()
664                .into_payload()
665                .into_unique_version();
666            vault_balance.put(LiquidFungibleResource::new(amount));
667            let updated_substate_content =
668                FungibleVaultBalanceFieldPayload::from_content_source(vault_balance)
669                    .into_unlocked_substate();
670            track
671                .set_substate(
672                    node_id,
673                    MAIN_BASE_PARTITION,
674                    substate_key,
675                    IndexedScryptoValue::from_typed(&updated_substate_content),
676                    &mut |_| -> Result<(), ()> { Ok(()) },
677                )
678                .unwrap();
679            events.push((
680                EventTypeIdentifier(
681                    Emitter::Method(node_id, ModuleId::Main),
682                    DepositEvent::EVENT_NAME.to_string(),
683                ),
684                scrypto_encode(&DepositEvent { amount }).unwrap(),
685            ));
686        }
687
688        // Take fee payments
689        let (fee_reserve_finalization, costing_parameters, transaction_costing_parameters) =
690            fee_reserve.finalize();
691        let mut fee_payments: IndexMap<NodeId, Decimal> = index_map_new();
692        let mut required = fee_reserve_finalization.total_cost();
693        let mut collected_fees = LiquidFungibleResource::new(Decimal::ZERO);
694        for (vault_id, mut locked, contingent) in
695            fee_reserve_finalization.locked_fees.iter().cloned().rev()
696        {
697            let amount = if contingent {
698                if is_success {
699                    Decimal::min(locked.amount(), required)
700                } else {
701                    Decimal::zero()
702                }
703            } else {
704                Decimal::min(locked.amount(), required)
705            };
706
707            // NOTE: Decimal arithmetic operation safe unwrap.
708            // No chance to overflow considering current costing parameters
709
710            // Take fees
711            collected_fees.put(locked.take_by_amount(amount).unwrap());
712            required = required.checked_sub(amount).unwrap();
713
714            // Refund overpayment
715            let mut vault_balance = track
716                .read_substate(
717                    &vault_id,
718                    MAIN_BASE_PARTITION,
719                    &FungibleVaultField::Balance.into(),
720                )
721                .unwrap()
722                .as_typed::<FungibleVaultBalanceFieldSubstate>()
723                .unwrap()
724                .into_payload()
725                .into_unique_version();
726            vault_balance.put(locked);
727            let updated_substate_content =
728                FungibleVaultBalanceFieldPayload::from_content_source(vault_balance)
729                    .into_unlocked_substate();
730            track
731                .set_substate(
732                    vault_id,
733                    MAIN_BASE_PARTITION,
734                    FungibleVaultField::Balance.into(),
735                    IndexedScryptoValue::from_typed(&updated_substate_content),
736                    &mut |_| -> Result<(), ()> { Ok(()) },
737                )
738                .unwrap();
739
740            // Record final payments
741            let entry = fee_payments.entry(vault_id).or_default();
742            *entry = entry.checked_add(amount).unwrap();
743
744            events.push((
745                EventTypeIdentifier(
746                    Emitter::Method(vault_id, ModuleId::Main),
747                    PayFeeEvent::EVENT_NAME.to_string(),
748                ),
749                scrypto_encode(&PayFeeEvent { amount }).unwrap(),
750            ));
751        }
752        // Free credit is locked first and thus used last
753        let free_credit = transaction_costing_parameters.free_credit_in_xrd;
754        if free_credit.is_positive() {
755            let amount = Decimal::min(free_credit, required);
756            collected_fees.put(LiquidFungibleResource::new(amount));
757            required = required.checked_sub(amount).unwrap();
758        }
759
760        let to_proposer = fee_reserve_finalization.to_proposer_amount();
761        let to_validator_set = fee_reserve_finalization.to_validator_set_amount();
762        let to_burn = fee_reserve_finalization.to_burn_amount();
763
764        // Sanity checks
765        assert!(
766            fee_reserve_finalization.total_bad_debt_in_xrd == Decimal::ZERO,
767            "Bad debt is non-zero: {}",
768            fee_reserve_finalization.total_bad_debt_in_xrd
769        );
770        assert!(
771            required == Decimal::ZERO,
772            "Locked fee does not cover transaction cost: {} required",
773            required
774        );
775        let remaining_collected_fees = collected_fees.amount().checked_sub(fee_reserve_finalization.total_royalty_cost_in_xrd /* royalty already distributed */).unwrap();
776        let to_distribute = to_proposer
777            .checked_add(to_validator_set)
778            .unwrap()
779            .checked_add(to_burn)
780            .unwrap();
781        assert!(
782            remaining_collected_fees  == to_distribute,
783            "Remaining collected fee isn't equal to amount to distribute (proposer/validator set/burn): {} != {}",
784            remaining_collected_fees,
785            to_distribute,
786        );
787
788        if !to_proposer.is_zero() || !to_validator_set.is_zero() {
789            // Fetch current leader
790            // TODO: maybe we should move current leader into validator rewards?
791            let substate: FieldSubstate<ConsensusManagerStateFieldPayload> = track
792                .read_substate(
793                    CONSENSUS_MANAGER.as_node_id(),
794                    MAIN_BASE_PARTITION,
795                    &ConsensusManagerField::State.into(),
796                )
797                .unwrap()
798                .as_typed()
799                .unwrap();
800            let current_leader = substate.into_payload().into_unique_version().current_leader;
801
802            // Update validator rewards
803            let substate: FieldSubstate<ConsensusManagerValidatorRewardsFieldPayload> = track
804                .read_substate(
805                    CONSENSUS_MANAGER.as_node_id(),
806                    MAIN_BASE_PARTITION,
807                    &ConsensusManagerField::ValidatorRewards.into(),
808                )
809                .unwrap()
810                .as_typed()
811                .unwrap();
812
813            let mut rewards = substate.into_payload().into_unique_version();
814
815            if let Some(current_leader) = current_leader {
816                let entry = rewards.proposer_rewards.entry(current_leader).or_default();
817                *entry = entry.checked_add(to_proposer).unwrap()
818            } else {
819                // If there is no current leader, the rewards go to the pool
820            };
821            let vault_node_id = rewards.rewards_vault.0 .0;
822
823            track
824                .set_substate(
825                    CONSENSUS_MANAGER.into_node_id(),
826                    MAIN_BASE_PARTITION,
827                    ConsensusManagerField::ValidatorRewards.into(),
828                    IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(
829                        ConsensusManagerValidatorRewardsFieldPayload::from_content_source(rewards),
830                    )),
831                    &mut |_| -> Result<(), ()> { Ok(()) },
832                )
833                .unwrap();
834
835            // Put validator rewards into the vault
836            let total_amount = to_proposer.checked_add(to_validator_set).unwrap();
837            let mut vault_balance = track
838                .read_substate(
839                    &vault_node_id,
840                    MAIN_BASE_PARTITION,
841                    &FungibleVaultField::Balance.into(),
842                )
843                .unwrap()
844                .as_typed::<FungibleVaultBalanceFieldSubstate>()
845                .unwrap()
846                .into_payload()
847                .into_unique_version();
848            vault_balance.put(collected_fees.take_by_amount(total_amount).unwrap());
849            let updated_substate_content =
850                FungibleVaultBalanceFieldPayload::from_content_source(vault_balance)
851                    .into_unlocked_substate();
852            track
853                .set_substate(
854                    vault_node_id,
855                    MAIN_BASE_PARTITION,
856                    FungibleVaultField::Balance.into(),
857                    IndexedScryptoValue::from_typed(&updated_substate_content),
858                    &mut |_| -> Result<(), ()> { Ok(()) },
859                )
860                .unwrap();
861
862            events.push((
863                EventTypeIdentifier(
864                    Emitter::Method(vault_node_id, ModuleId::Main),
865                    DepositEvent::EVENT_NAME.to_string(),
866                ),
867                scrypto_encode(&DepositEvent {
868                    amount: total_amount,
869                })
870                .unwrap(),
871            ));
872        }
873
874        if to_burn.is_positive() {
875            events.push((
876                EventTypeIdentifier(
877                    Emitter::Method(XRD.into_node_id(), ModuleId::Main),
878                    "BurnFungibleResourceEvent".to_string(),
879                ),
880                scrypto_encode(&BurnFungibleResourceEvent { amount: to_burn }).unwrap(),
881            ));
882        }
883
884        (
885            fee_reserve_finalization,
886            fee_payments,
887            events,
888            costing_parameters,
889            transaction_costing_parameters,
890        )
891    }
892
893    fn update_transaction_tracker<S: SubstateDatabase>(
894        track: &mut Track<S>,
895        next_epoch: Epoch,
896        intent_hash_nullifications: Vec<IntentHashNullification>,
897        is_success: bool,
898    ) -> Vec<Nullification> {
899        // NOTE: In the case of system transactions, we could skip most of this...
900        //       except for backwards compatibility, we can't!
901
902        // Read the intent hash store
903        let mut transaction_tracker = track
904            .read_substate(
905                TRANSACTION_TRACKER.as_node_id(),
906                MAIN_BASE_PARTITION,
907                &TransactionTrackerField::TransactionTracker.into(),
908            )
909            .unwrap()
910            .as_typed::<FieldSubstate<TransactionTrackerSubstate>>()
911            .unwrap()
912            .into_payload()
913            .into_v1();
914
915        let mut performed_nullifications = vec![];
916
917        for intent_hash_nullification in intent_hash_nullifications {
918            let Some(nullification) = Nullification::of_intent(
919                intent_hash_nullification,
920                Epoch::of(transaction_tracker.start_epoch),
921                is_success,
922            ) else {
923                continue;
924            };
925            let (expiry_epoch, hash) = nullification.transaction_tracker_keys();
926            performed_nullifications.push(nullification);
927
928            let partition_number = transaction_tracker.partition_for_expiry_epoch(expiry_epoch)
929                .expect("Validation of the max expiry epoch window combined with the current epoch check on launch should ensure that the expiry epoch is in range for the transaction tracker");
930
931            // Update the status of the intent hash
932            track
933                .set_substate(
934                    TRANSACTION_TRACKER.into_node_id(),
935                    PartitionNumber(partition_number),
936                    SubstateKey::Map(scrypto_encode(&hash).unwrap()),
937                    IndexedScryptoValue::from_typed(&KeyValueEntrySubstate::V1(
938                        KeyValueEntrySubstateV1 {
939                            value: Some(if is_success {
940                                TransactionStatus::V1(TransactionStatusV1::CommittedSuccess)
941                            } else {
942                                TransactionStatus::V1(TransactionStatusV1::CommittedFailure)
943                            }),
944                            // TODO: maybe make it immutable, but how does this affect partition deletion?
945                            lock_status: LockStatus::Unlocked,
946                        },
947                    )),
948                    &mut |_| -> Result<(), ()> { Ok(()) },
949                )
950                .unwrap();
951        }
952
953        // Check if all intent hashes in the first epoch have expired, based on the `next_epoch`.
954        //
955        // In this particular implementation, because the transaction tracker coverage is greater than
956        // the max epoch range in transaction header, we must check epoch range first to
957        // ensure we don't store intent hash too far into the future.
958        //
959        // Also, we need to make sure epoch doesn't jump by a large distance.
960        if next_epoch.number()
961            >= transaction_tracker.start_epoch + transaction_tracker.epochs_per_partition
962        {
963            let discarded_partition = transaction_tracker.advance();
964            track.delete_partition(
965                TRANSACTION_TRACKER.as_node_id(),
966                PartitionNumber(discarded_partition),
967            );
968        }
969        track
970            .set_substate(
971                TRANSACTION_TRACKER.into_node_id(),
972                MAIN_BASE_PARTITION,
973                TransactionTrackerField::TransactionTracker.into(),
974                IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(
975                    TransactionTrackerSubstate::V1(transaction_tracker),
976                )),
977                &mut |_| -> Result<(), ()> { Ok(()) },
978            )
979            .unwrap();
980
981        performed_nullifications
982    }
983
984    #[cfg(not(feature = "alloc"))]
985    fn print_execution_summary(receipt: &TransactionReceipt) {
986        // NB - we use "to_string" to ensure they align correctly
987
988        if let Some(fee_details) = &receipt.fee_details {
989            println!("{:-^120}", "Execution Cost Breakdown");
990            for (k, v) in &fee_details.execution_cost_breakdown {
991                println!("{:<75}: {:>25}", k, v.to_string());
992            }
993
994            println!("{:-^120}", "Finalization Cost Breakdown");
995            for (k, v) in &fee_details.finalization_cost_breakdown {
996                println!("{:<75}: {:>25}", k, v.to_string());
997            }
998        }
999
1000        println!("{:-^120}", "Fee Summary");
1001        println!(
1002            "{:<40}: {:>25}",
1003            "Execution Cost Units Consumed",
1004            receipt
1005                .fee_summary
1006                .total_execution_cost_units_consumed
1007                .to_string()
1008        );
1009        println!(
1010            "{:<40}: {:>25}",
1011            "Finalization Cost Units Consumed",
1012            receipt
1013                .fee_summary
1014                .total_finalization_cost_units_consumed
1015                .to_string()
1016        );
1017        println!(
1018            "{:<40}: {:>25}",
1019            "Execution Cost in XRD",
1020            receipt.fee_summary.total_execution_cost_in_xrd.to_string()
1021        );
1022        println!(
1023            "{:<40}: {:>25}",
1024            "Finalization Cost in XRD",
1025            receipt
1026                .fee_summary
1027                .total_finalization_cost_in_xrd
1028                .to_string()
1029        );
1030        println!(
1031            "{:<40}: {:>25}",
1032            "Tipping Cost in XRD",
1033            receipt.fee_summary.total_tipping_cost_in_xrd.to_string()
1034        );
1035        println!(
1036            "{:<40}: {:>25}",
1037            "Storage Cost in XRD",
1038            receipt.fee_summary.total_storage_cost_in_xrd.to_string()
1039        );
1040        println!(
1041            "{:<40}: {:>25}",
1042            "Royalty Costs in XRD",
1043            receipt.fee_summary.total_royalty_cost_in_xrd.to_string()
1044        );
1045
1046        match &receipt.result {
1047            TransactionResult::Commit(commit) => {
1048                println!("{:-^120}", "Application Logs");
1049                for (level, message) in &commit.application_logs {
1050                    println!("[{}] {}", level, message);
1051                }
1052
1053                println!("{:-^120}", "Outcome");
1054                println!(
1055                    "{}",
1056                    match &commit.outcome {
1057                        TransactionOutcome::Success(_) => "Success".to_string(),
1058                        TransactionOutcome::Failure(error) => format!("Failure: {:?}", error),
1059                    }
1060                );
1061            }
1062            TransactionResult::Reject(e) => {
1063                println!("{:-^120}", "Transaction Rejected");
1064                println!("{:?}", e.reason);
1065            }
1066            TransactionResult::Abort(e) => {
1067                println!("{:-^120}", "Transaction Aborted");
1068                println!("{:?}", e);
1069            }
1070        }
1071        println!("{:-^120}", "Finish");
1072    }
1073
1074    fn reference_check(
1075        references: &IndexSet<Reference>,
1076        modules: &mut SystemModuleMixer,
1077        store: &mut impl CommitableSubstateStore,
1078        always_visible_global_nodes: &IndexSet<NodeId>,
1079    ) -> Result<(IndexSet<GlobalAddress>, IndexSet<InternalAddress>), BootloadingError> {
1080        let mut global_addresses = indexset!();
1081        let mut direct_accesses = indexset!();
1082
1083        // Check references
1084        for reference in references.iter() {
1085            let node_id = &reference.0;
1086
1087            if always_visible_global_nodes.contains(node_id) {
1088                // Allow always visible node and do not add reference
1089                continue;
1090            }
1091
1092            if node_id.is_global_preallocated() {
1093                // Allow global virtual and add reference
1094                global_addresses.insert(GlobalAddress::new_or_panic((*node_id).into()));
1095                continue;
1096            }
1097
1098            let ref_value = store
1099                .read_substate(
1100                    node_id,
1101                    TYPE_INFO_FIELD_PARTITION,
1102                    &TypeInfoField::TypeInfo.into(),
1103                )
1104                .ok_or_else(|| BootloadingError::ReferencedNodeDoesNotExist((*node_id).into()))?;
1105
1106            match Self::verify_boot_ref_value(modules, node_id, ref_value)? {
1107                StableReferenceType::Global => {
1108                    global_addresses.insert(GlobalAddress::new_or_panic((*node_id).into()));
1109                }
1110                StableReferenceType::DirectAccess => {
1111                    direct_accesses.insert(InternalAddress::new_or_panic((*node_id).into()));
1112                }
1113            }
1114        }
1115
1116        Ok((global_addresses, direct_accesses))
1117    }
1118
1119    /// Checks that references exist in the store
1120    fn build_call_frame_inits_with_reference_check<'a>(
1121        intents: impl Iterator<Item = &'a ExecutableIntent>,
1122        modules: &mut SystemModuleMixer,
1123        store: &mut impl CommitableSubstateStore,
1124        always_visible_global_nodes: &'static IndexSet<NodeId>,
1125    ) -> Result<Vec<CallFrameInit<Actor>>, BootloadingError> {
1126        let mut init_call_frames = vec![];
1127        for (index, intent) in intents.enumerate() {
1128            let (global_addresses, direct_accesses) = Self::reference_check(
1129                &intent.references,
1130                modules,
1131                store,
1132                always_visible_global_nodes,
1133            )?;
1134
1135            init_call_frames.push(CallFrameInit {
1136                data: Actor::Root,
1137                global_addresses,
1138                direct_accesses,
1139                always_visible_global_nodes,
1140                stack_id: index,
1141            });
1142        }
1143
1144        Ok(init_call_frames)
1145    }
1146
1147    fn verify_boot_ref_value(
1148        modules: &mut SystemModuleMixer,
1149        node_id: &NodeId,
1150        ref_value: &IndexedScryptoValue,
1151    ) -> Result<StableReferenceType, BootloadingError> {
1152        if let Some(costing) = modules.costing_mut() {
1153            let io_access = IOAccess::ReadFromDb(
1154                CanonicalSubstateKey {
1155                    node_id: *node_id,
1156                    partition_number: TYPE_INFO_FIELD_PARTITION,
1157                    substate_key: SubstateKey::Field(TypeInfoField::TypeInfo.field_index()),
1158                },
1159                ref_value.len(),
1160            );
1161            let event = CheckReferenceEvent::IOAccess(&io_access);
1162
1163            costing
1164                .apply_deferred_execution_cost(ExecutionCostingEntry::CheckReference {
1165                    event: &event,
1166                })
1167                .map_err(BootloadingError::FailedToApplyDeferredCosts)?;
1168        }
1169
1170        let type_substate: TypeInfoSubstate = ref_value.as_typed().unwrap();
1171        match &type_substate {
1172            TypeInfoSubstate::Object(
1173                info @ ObjectInfo {
1174                    blueprint_info: BlueprintInfo { blueprint_id, .. },
1175                    ..
1176                },
1177            ) => {
1178                if info.is_global() {
1179                    Ok(StableReferenceType::Global)
1180                } else if blueprint_id.package_address.eq(&RESOURCE_PACKAGE)
1181                    && (blueprint_id.blueprint_name.eq(FUNGIBLE_VAULT_BLUEPRINT)
1182                        || blueprint_id.blueprint_name.eq(NON_FUNGIBLE_VAULT_BLUEPRINT))
1183                {
1184                    Ok(StableReferenceType::DirectAccess)
1185                } else {
1186                    Err(BootloadingError::ReferencedNodeDoesNotAllowDirectAccess(
1187                        (*node_id).into(),
1188                    ))
1189                }
1190            }
1191            _ => Err(BootloadingError::ReferencedNodeIsNotAnObject(
1192                (*node_id).into(),
1193            )),
1194        }
1195    }
1196
1197    fn create_non_commit_receipt(
1198        result: TransactionResult,
1199        print_execution_summary: bool,
1200        costing_module: CostingModule,
1201    ) -> TransactionReceipt {
1202        let (fee_reserve, cost_breakdown, detailed_cost_breakdown) =
1203            costing_module.unpack_for_receipt();
1204        let (finalization_summary, costing_parameters, transaction_costing_parameters) =
1205            fee_reserve.finalize();
1206        let fee_summary = finalization_summary.into();
1207
1208        Self::create_receipt_internal(
1209            print_execution_summary,
1210            costing_parameters,
1211            cost_breakdown,
1212            detailed_cost_breakdown,
1213            transaction_costing_parameters,
1214            fee_summary,
1215            result,
1216        )
1217    }
1218
1219    fn create_rejection_receipt(
1220        reason: impl Into<RejectionReason>,
1221        modules: SystemModuleMixer,
1222    ) -> TransactionReceipt {
1223        Self::create_non_commit_receipt(
1224            TransactionResult::Reject(RejectResult {
1225                reason: reason.into(),
1226            }),
1227            modules.is_kernel_trace_enabled(),
1228            modules.unpack_costing(),
1229        )
1230    }
1231
1232    fn create_abort_receipt(
1233        reason: impl Into<AbortReason>,
1234        modules: SystemModuleMixer,
1235    ) -> TransactionReceipt {
1236        Self::create_non_commit_receipt(
1237            TransactionResult::Abort(AbortResult {
1238                reason: reason.into(),
1239            }),
1240            modules.is_kernel_trace_enabled(),
1241            modules.unpack_costing(),
1242        )
1243    }
1244
1245    fn create_commit_receipt<S: SubstateDatabase>(
1246        outcome: Result<Vec<InstructionOutput>, RuntimeError>,
1247        mut track: Track<S>,
1248        modules: SystemModuleMixer,
1249        system_finalization: SystemFinalization,
1250    ) -> TransactionReceipt {
1251        let print_execution_summary = modules.is_kernel_trace_enabled();
1252        let execution_trace_enabled = modules.is_execution_trace_enabled();
1253        let (costing_module, runtime_module, execution_trace_module) = modules.unpack();
1254        let (mut fee_reserve, cost_breakdown, detailed_cost_breakdown) =
1255            costing_module.unpack_for_receipt();
1256        let is_success = outcome.is_ok();
1257
1258        // Commit/revert
1259        if !is_success {
1260            fee_reserve.revert_royalty();
1261            track.revert_non_force_write_changes();
1262        }
1263
1264        // Distribute fees
1265        let (
1266            fee_reserve_finalization,
1267            paying_vaults,
1268            finalization_events,
1269            costing_parameters,
1270            transaction_costing_parameters,
1271        ) = Self::finalize_fees_for_commit(&mut track, fee_reserve, is_success);
1272
1273        let fee_destination = FeeDestination {
1274            to_proposer: fee_reserve_finalization.to_proposer_amount(),
1275            to_validator_set: fee_reserve_finalization.to_validator_set_amount(),
1276            to_burn: fee_reserve_finalization.to_burn_amount(),
1277            to_royalty_recipients: fee_reserve_finalization.royalty_cost_breakdown.clone(),
1278        };
1279
1280        // Update intent hash status
1281        let performed_nullifications =
1282            if let Some(next_epoch) = Self::read_epoch_uncosted(&mut track) {
1283                Self::update_transaction_tracker(
1284                    &mut track,
1285                    next_epoch,
1286                    system_finalization.intent_nullifications,
1287                    is_success,
1288                )
1289            } else {
1290                vec![]
1291            };
1292
1293        // Finalize events and logs
1294        let (mut application_events, application_logs) = runtime_module.finalize(is_success);
1295        application_events.extend(finalization_events);
1296
1297        // Finalize track
1298        let (tracked_substates, substate_db) = {
1299            match track.finalize() {
1300                Ok(result) => result,
1301                Err(TrackFinalizeError::TransientSubstateOwnsNode) => {
1302                    panic!("System invariants should prevent transient substate from owning nodes");
1303                }
1304            }
1305        };
1306
1307        // Generate state updates from tracked substates
1308        // Note that this process will prune invalid reads
1309        let (new_node_ids, state_updates) = tracked_substates.to_state_updates();
1310
1311        // Summarizes state updates
1312        let system_structure =
1313            SystemStructure::resolve(substate_db, &state_updates, &application_events);
1314        let state_update_summary =
1315            StateUpdateSummary::new(substate_db, new_node_ids, &state_updates);
1316
1317        // Resource reconciliation does not currently work in preview mode
1318        if transaction_costing_parameters.free_credit_in_xrd.is_zero() {
1319            reconcile_resource_state_and_events(
1320                &state_update_summary,
1321                &application_events,
1322                SystemDatabaseReader::new_with_overlay(substate_db, &state_updates),
1323            );
1324        }
1325
1326        let execution_trace = if execution_trace_enabled {
1327            Some(execution_trace_module.finalize(&paying_vaults, is_success))
1328        } else {
1329            None
1330        };
1331
1332        let fee_summary = fee_reserve_finalization.into();
1333        let result = TransactionResult::Commit(CommitResult {
1334            state_updates,
1335            state_update_summary,
1336            fee_source: FeeSource { paying_vaults },
1337            fee_destination,
1338            outcome: match outcome {
1339                Ok(o) => TransactionOutcome::Success(o),
1340                Err(e) => TransactionOutcome::Failure(e),
1341            },
1342            application_events,
1343            application_logs,
1344            system_structure,
1345            execution_trace,
1346            performed_nullifications,
1347        });
1348
1349        Self::create_receipt_internal(
1350            print_execution_summary,
1351            costing_parameters,
1352            cost_breakdown,
1353            detailed_cost_breakdown,
1354            transaction_costing_parameters,
1355            fee_summary,
1356            result,
1357        )
1358    }
1359
1360    #[cfg_attr(feature = "alloc", allow(unused_variables))]
1361    fn create_receipt_internal(
1362        print_execution_summary: bool,
1363        costing_parameters: CostingParameters,
1364        cost_breakdown: Option<CostBreakdown>,
1365        detailed_cost_breakdown: Option<DetailedCostBreakdown>,
1366        transaction_costing_parameters: TransactionCostingParameters,
1367        fee_summary: TransactionFeeSummary,
1368        result: TransactionResult,
1369    ) -> TransactionReceipt {
1370        let transaction_costing_parameters = TransactionCostingParametersReceiptV2 {
1371            tip_proportion: transaction_costing_parameters.tip.proportion(),
1372            free_credit_in_xrd: transaction_costing_parameters.free_credit_in_xrd,
1373        };
1374
1375        let fee_details = cost_breakdown.map(|b| TransactionFeeDetails {
1376            execution_cost_breakdown: b.execution_cost_breakdown.into_iter().collect(),
1377            finalization_cost_breakdown: b.finalization_cost_breakdown.into_iter().collect(),
1378        });
1379
1380        let debug_information = detailed_cost_breakdown.map(|b| TransactionDebugInformation {
1381            detailed_execution_cost_breakdown: b.detailed_execution_cost_breakdown,
1382        });
1383
1384        let receipt = TransactionReceipt {
1385            costing_parameters,
1386            transaction_costing_parameters,
1387            fee_summary,
1388            fee_details,
1389            result,
1390            resources_usage: None,
1391            debug_information,
1392        };
1393
1394        // Dump summary
1395        #[cfg(not(feature = "alloc"))]
1396        if print_execution_summary {
1397            Self::print_execution_summary(&receipt);
1398        }
1399
1400        receipt
1401    }
1402
1403    fn resolve_modules(
1404        executable: &ExecutableTransaction,
1405        init_input: SystemSelfInit,
1406    ) -> Result<SystemModuleMixer, TransactionReceiptV1> {
1407        let mut system_parameters = init_input.system_parameters;
1408        let system_logic_version = init_input.system_logic_version;
1409
1410        let mut enabled_modules = {
1411            let mut enabled_modules = EnabledModules::AUTH | EnabledModules::TRANSACTION_RUNTIME;
1412            if !executable.disable_limits_and_costing_modules() {
1413                enabled_modules |= EnabledModules::LIMITS;
1414                enabled_modules |= EnabledModules::COSTING;
1415            };
1416
1417            if init_input.enable_kernel_trace {
1418                enabled_modules |= EnabledModules::KERNEL_TRACE;
1419            }
1420            if init_input.execution_trace.is_some() {
1421                enabled_modules |= EnabledModules::EXECUTION_TRACE;
1422            }
1423
1424            enabled_modules
1425        };
1426
1427        let mut abort_when_loan_repaid = false;
1428
1429        // Override system configuration
1430        if let Some(system_overrides) = &init_input.system_overrides {
1431            if let Some(costing_override) = &system_overrides.costing_parameters {
1432                system_parameters.costing_parameters = *costing_override;
1433            }
1434
1435            if let Some(limits_override) = &system_overrides.limit_parameters {
1436                system_parameters.limit_parameters = *limits_override;
1437            }
1438
1439            if let Some(network_definition) = &system_overrides.network_definition {
1440                system_parameters.network_definition = network_definition.clone();
1441            }
1442
1443            if system_overrides.disable_auth {
1444                enabled_modules.remove(EnabledModules::AUTH);
1445            }
1446
1447            if system_overrides.disable_costing {
1448                enabled_modules.remove(EnabledModules::COSTING);
1449            }
1450
1451            if system_overrides.disable_limits {
1452                enabled_modules.remove(EnabledModules::LIMITS);
1453            }
1454
1455            if system_overrides.abort_when_loan_repaid {
1456                abort_when_loan_repaid = true;
1457            }
1458        }
1459
1460        let costing_module = CostingModule {
1461            current_depth: 0,
1462            fee_reserve: SystemLoanFeeReserve::new(
1463                system_parameters.costing_parameters,
1464                executable.costing_parameters().clone(),
1465                abort_when_loan_repaid,
1466            ),
1467            fee_table: FeeTable::new(system_logic_version),
1468            tx_payload_len: executable.payload_size(),
1469            tx_num_of_signature_validations: executable.num_of_signature_validations(),
1470            config: system_parameters.costing_module_config,
1471            cost_breakdown: if init_input.enable_cost_breakdown {
1472                Some(Default::default())
1473            } else {
1474                None
1475            },
1476            detailed_cost_breakdown: if init_input.enable_debug_information {
1477                Some(Default::default())
1478            } else {
1479                None
1480            },
1481            on_apply_cost: Default::default(),
1482        };
1483
1484        let auth_module = system_logic_version
1485            .create_auth_module(executable)
1486            .map_err(|reason| {
1487                let print_execution_summary =
1488                    enabled_modules.contains(EnabledModules::KERNEL_TRACE);
1489                Self::create_non_commit_receipt(
1490                    TransactionResult::Reject(RejectResult { reason }),
1491                    print_execution_summary,
1492                    costing_module.clone(),
1493                )
1494            })?;
1495
1496        let module_mixer = SystemModuleMixer::new(
1497            enabled_modules,
1498            KernelTraceModule,
1499            TransactionRuntimeModule::new(
1500                system_parameters.network_definition,
1501                *executable.unique_hash(),
1502            ),
1503            auth_module,
1504            LimitsModule::from_params(system_parameters.limit_parameters),
1505            costing_module,
1506            ExecutionTraceModule::new(init_input.execution_trace.unwrap_or(0)),
1507        );
1508
1509        Ok(module_mixer)
1510    }
1511}
1512
1513impl<V: SystemCallbackObject> KernelTransactionExecutor for System<V> {
1514    type Init = SystemInit<V::Init>;
1515    type Executable = ExecutableTransaction;
1516    type ExecutionOutput = Vec<InstructionOutput>;
1517    type Receipt = TransactionReceipt;
1518
1519    fn init(
1520        store: &mut impl CommitableSubstateStore,
1521        executable: &ExecutableTransaction,
1522        init_input: Self::Init,
1523        always_visible_global_nodes: &'static IndexSet<NodeId>,
1524    ) -> Result<(Self, Vec<CallFrameInit<Actor>>), Self::Receipt> {
1525        // Dump executable
1526        #[cfg(not(feature = "alloc"))]
1527        if init_input.self_init.enable_kernel_trace {
1528            Self::print_executable(executable);
1529        }
1530
1531        let logic_version = init_input.self_init.system_logic_version;
1532        let mut modules = Self::resolve_modules(executable, init_input.self_init)?;
1533
1534        // NOTE: Have to use match pattern rather than map_err to appease the borrow checker
1535        let callback = match V::init(init_input.callback_init) {
1536            Ok(callback) => callback,
1537            Err(error) => return Err(Self::create_rejection_receipt(error, modules)),
1538        };
1539
1540        match modules.init() {
1541            Ok(()) => {}
1542            Err(error) => return Err(Self::create_rejection_receipt(error, modules)),
1543        }
1544
1545        // Perform runtime validation.
1546        // NOTE: The epoch doesn't exist yet on the very first transaction, so we skip this
1547        if let Some(current_epoch) = Self::read_epoch_uncosted(store) {
1548            // We are assuming that intent hash store is ready when epoch manager is ready.
1549            if let Some(range) = executable.overall_epoch_range() {
1550                let epoch_validation_result = Self::validate_epoch_range(
1551                    current_epoch,
1552                    range.start_epoch_inclusive,
1553                    range.end_epoch_exclusive,
1554                );
1555                match epoch_validation_result {
1556                    Ok(()) => {}
1557                    Err(error) => return Err(Self::create_rejection_receipt(error, modules)),
1558                }
1559            }
1560        }
1561
1562        for hash_nullification in executable.intent_hash_nullifications() {
1563            let intent_hash_validation_result = match hash_nullification {
1564                IntentHashNullification::TransactionIntent {
1565                    intent_hash,
1566                    expiry_epoch,
1567                } => Self::validate_intent_hash_uncosted(
1568                    store,
1569                    IntentHash::Transaction(*intent_hash),
1570                    *expiry_epoch,
1571                ),
1572                IntentHashNullification::SimulatedTransactionIntent { .. } => {
1573                    // No validation is done on a simulated transaction intent used during preview
1574                    Ok(())
1575                }
1576                IntentHashNullification::Subintent {
1577                    intent_hash,
1578                    expiry_epoch,
1579                } => Self::validate_intent_hash_uncosted(
1580                    store,
1581                    IntentHash::Subintent(*intent_hash),
1582                    *expiry_epoch,
1583                ),
1584                IntentHashNullification::SimulatedSubintent { .. } => {
1585                    // No validation is done on a simulated subintent used during preview
1586                    Ok(())
1587                }
1588            }
1589            .and_then(|_| {
1590                let charge_for_nullification_check = match hash_nullification {
1591                    IntentHashNullification::TransactionIntent { .. }
1592                    | IntentHashNullification::SimulatedTransactionIntent { .. } => {
1593                        logic_version.should_charge_for_transaction_intent()
1594                    }
1595                    IntentHashNullification::Subintent { .. }
1596                    | IntentHashNullification::SimulatedSubintent { .. } => true,
1597                };
1598
1599                if charge_for_nullification_check {
1600                    if let Some(costing) = modules.costing_mut() {
1601                        return costing
1602                            .apply_deferred_execution_cost(
1603                                ExecutionCostingEntry::CheckIntentValidity,
1604                            )
1605                            .map_err(|e| {
1606                                RejectionReason::BootloadingError(
1607                                    BootloadingError::FailedToApplyDeferredCosts(e),
1608                                )
1609                            });
1610                    }
1611                }
1612
1613                Ok(())
1614            });
1615
1616            match intent_hash_validation_result {
1617                Ok(()) => {}
1618                Err(error) => return Err(Self::create_rejection_receipt(error, modules)),
1619            }
1620        }
1621
1622        if let Some(range) = executable.overall_proposer_timestamp_range() {
1623            if range.start_timestamp_inclusive.is_some() || range.end_timestamp_exclusive.is_some()
1624            {
1625                let substate: ConsensusManagerProposerMilliTimestampFieldSubstate = store
1626                    .read_substate(
1627                        CONSENSUS_MANAGER.as_node_id(),
1628                        MAIN_BASE_PARTITION,
1629                        &ConsensusManagerField::ProposerMilliTimestamp.into(),
1630                    )
1631                    .unwrap()
1632                    .as_typed()
1633                    .unwrap();
1634                let current_time = Instant::new(
1635                    substate
1636                        .into_payload()
1637                        .fully_update_and_into_latest_version()
1638                        .epoch_milli
1639                        / 1000,
1640                );
1641                if let Some(start_timestamp_inclusive) = range.start_timestamp_inclusive {
1642                    if current_time < start_timestamp_inclusive {
1643                        return Err(Self::create_rejection_receipt(
1644                            RejectionReason::TransactionProposerTimestampNotYetValid {
1645                                valid_from_inclusive: start_timestamp_inclusive,
1646                                current_time,
1647                            },
1648                            modules,
1649                        ));
1650                    }
1651                }
1652
1653                if let Some(end_timestamp_exclusive) = range.end_timestamp_exclusive {
1654                    if current_time >= end_timestamp_exclusive {
1655                        return Err(Self::create_rejection_receipt(
1656                            RejectionReason::TransactionProposerTimestampNoLongerValid {
1657                                valid_to_exclusive: end_timestamp_exclusive,
1658                                current_time,
1659                            },
1660                            modules,
1661                        ));
1662                    }
1663                }
1664
1665                if let Some(costing) = modules.costing_mut() {
1666                    if let Err(error) =
1667                        costing.apply_deferred_execution_cost(ExecutionCostingEntry::CheckTimestamp)
1668                    {
1669                        return Err(Self::create_rejection_receipt(
1670                            RejectionReason::BootloadingError(
1671                                BootloadingError::FailedToApplyDeferredCosts(error),
1672                            ),
1673                            modules,
1674                        ));
1675                    }
1676                }
1677            }
1678        }
1679
1680        let call_frame_inits = match Self::build_call_frame_inits_with_reference_check(
1681            executable.all_intents(),
1682            &mut modules,
1683            store,
1684            always_visible_global_nodes,
1685        ) {
1686            Ok(call_frame_inits) => call_frame_inits,
1687            Err(error) => return Err(Self::create_rejection_receipt(error, modules)),
1688        };
1689
1690        let system = System::new(
1691            logic_version,
1692            callback,
1693            modules,
1694            SystemFinalization {
1695                intent_nullifications: executable.intent_hash_nullifications().to_vec(),
1696            },
1697        );
1698
1699        Ok((system, call_frame_inits))
1700    }
1701
1702    fn execute<Y: SystemBasedKernelApi>(
1703        api: &mut Y,
1704        executable: &ExecutableTransaction,
1705    ) -> Result<Vec<InstructionOutput>, RuntimeError> {
1706        let mut system_service = SystemService::new(api);
1707
1708        // Allocate global addresses
1709        let mut global_address_reservations = Vec::new();
1710        for PreAllocatedAddress {
1711            blueprint_id,
1712            address,
1713        } in executable.pre_allocated_addresses()
1714        {
1715            let global_address_reservation =
1716                system_service.prepare_global_address(blueprint_id.clone(), *address)?;
1717            global_address_reservations.push(global_address_reservation);
1718        }
1719
1720        let system_logic_version = system_service.system().versioned_system_logic;
1721
1722        let output = system_logic_version.execute_transaction(
1723            api,
1724            executable,
1725            global_address_reservations,
1726        )?;
1727
1728        Ok(output)
1729    }
1730
1731    fn finalize(
1732        &mut self,
1733        executable: &ExecutableTransaction,
1734        info: StoreCommitInfo,
1735    ) -> Result<(), RuntimeError> {
1736        self.modules.on_teardown()?;
1737
1738        // Note that if a transactions fails during this phase, the costing is
1739        // done as if it would succeed.
1740        for store_commit in &info {
1741            self.modules
1742                .apply_finalization_cost(FinalizationCostingEntry::CommitStateUpdates {
1743                    store_commit,
1744                })
1745                .map_err(RuntimeError::FinalizationCostingError)?;
1746        }
1747        self.modules
1748            .apply_finalization_cost(FinalizationCostingEntry::CommitEvents {
1749                events: &self.modules.events().clone(),
1750            })
1751            .map_err(RuntimeError::FinalizationCostingError)?;
1752        self.modules
1753            .apply_finalization_cost(FinalizationCostingEntry::CommitLogs {
1754                logs: &self.modules.logs().clone(),
1755            })
1756            .map_err(RuntimeError::FinalizationCostingError)?;
1757        let num_of_intent_statuses = executable
1758            .intent_hash_nullifications()
1759            .iter()
1760            .map(|n| match n {
1761                IntentHashNullification::TransactionIntent { .. }
1762                | IntentHashNullification::SimulatedTransactionIntent { .. } => {
1763                    if self
1764                        .versioned_system_logic
1765                        .should_charge_for_transaction_intent()
1766                    {
1767                        1
1768                    } else {
1769                        0
1770                    }
1771                }
1772                IntentHashNullification::Subintent { .. }
1773                | IntentHashNullification::SimulatedSubintent { .. } => 1,
1774            })
1775            .sum();
1776        self.modules
1777            .apply_finalization_cost(FinalizationCostingEntry::CommitIntentStatus {
1778                num_of_intent_statuses,
1779            })
1780            .map_err(RuntimeError::FinalizationCostingError)?;
1781
1782        /* state storage costs */
1783        for store_commit in &info {
1784            self.modules
1785                .apply_storage_cost(StorageType::State, store_commit.len_increase())
1786                .map_err(RuntimeError::FinalizationCostingError)?;
1787        }
1788
1789        /* archive storage costs */
1790        let total_event_size = self.modules.events().iter().map(|x| x.len()).sum();
1791        self.modules
1792            .apply_storage_cost(StorageType::Archive, total_event_size)
1793            .map_err(RuntimeError::FinalizationCostingError)?;
1794
1795        let total_log_size = self.modules.logs().iter().map(|x| x.1.len()).sum();
1796        self.modules
1797            .apply_storage_cost(StorageType::Archive, total_log_size)
1798            .map_err(RuntimeError::FinalizationCostingError)?;
1799
1800        Ok(())
1801    }
1802
1803    fn create_receipt<S: SubstateDatabase>(
1804        mut self,
1805        track: Track<S>,
1806        interpretation_result: Result<Vec<InstructionOutput>, TransactionExecutionError>,
1807    ) -> TransactionReceipt {
1808        // Panic if an error is encountered in the system layer or below. The following code
1809        // is only enabled when compiling with the standard library since the panic catching
1810        // machinery and `SystemPanic` errors are only implemented in `std`.
1811        #[cfg(feature = "std")]
1812        if let Err(TransactionExecutionError::RuntimeError(RuntimeError::SystemError(
1813            SystemError::SystemPanic(..),
1814        ))) = interpretation_result
1815        {
1816            panic!("An error has occurred in the system layer or below and thus the transaction executor has panicked. Error: \"{interpretation_result:?}\"")
1817        }
1818
1819        #[cfg(not(feature = "alloc"))]
1820        if self.modules.is_kernel_trace_enabled() {
1821            println!("{:-^120}", "Interpretation Results");
1822            println!("{:?}", interpretation_result);
1823        }
1824
1825        let result_type = Self::determine_result_type(
1826            interpretation_result,
1827            &mut self.modules.costing_mut_even_if_disabled().fee_reserve,
1828        );
1829
1830        match result_type {
1831            TransactionResultType::Reject(reason) => {
1832                Self::create_rejection_receipt(reason, self.modules)
1833            }
1834            TransactionResultType::Abort(reason) => {
1835                Self::create_abort_receipt(reason, self.modules)
1836            }
1837            TransactionResultType::Commit(outcome) => {
1838                Self::create_commit_receipt(outcome, track, self.modules, self.finalization)
1839            }
1840        }
1841    }
1842}
1843
1844impl<V: SystemCallbackObject> KernelCallbackObject for System<V> {
1845    type LockData = SystemLockData;
1846    type CallFrameData = Actor;
1847
1848    fn on_pin_node<Y: KernelInternalApi<System = Self>>(
1849        node_id: &NodeId,
1850        api: &mut Y,
1851    ) -> Result<(), RuntimeError> {
1852        SystemModuleMixer::on_pin_node(api, node_id)
1853    }
1854
1855    fn on_create_node<Y: KernelInternalApi<System = Self>>(
1856        event: CreateNodeEvent,
1857        api: &mut Y,
1858    ) -> Result<(), RuntimeError> {
1859        SystemModuleMixer::on_create_node(api, &event)
1860    }
1861
1862    fn on_drop_node<Y: KernelInternalApi<System = Self>>(
1863        event: DropNodeEvent,
1864        api: &mut Y,
1865    ) -> Result<(), RuntimeError> {
1866        SystemModuleMixer::on_drop_node(api, &event)
1867    }
1868
1869    fn on_move_module<Y: KernelInternalApi<System = Self>>(
1870        event: MoveModuleEvent,
1871        api: &mut Y,
1872    ) -> Result<(), RuntimeError> {
1873        SystemModuleMixer::on_move_module(api, &event)
1874    }
1875
1876    fn on_open_substate<Y: KernelInternalApi<System = Self>>(
1877        event: OpenSubstateEvent,
1878        api: &mut Y,
1879    ) -> Result<(), RuntimeError> {
1880        SystemModuleMixer::on_open_substate(api, &event)
1881    }
1882
1883    fn on_close_substate<Y: KernelInternalApi<System = Self>>(
1884        event: CloseSubstateEvent,
1885        api: &mut Y,
1886    ) -> Result<(), RuntimeError> {
1887        SystemModuleMixer::on_close_substate(api, &event)
1888    }
1889
1890    fn on_read_substate<Y: KernelInternalApi<System = Self>>(
1891        event: ReadSubstateEvent,
1892        api: &mut Y,
1893    ) -> Result<(), RuntimeError> {
1894        SystemModuleMixer::on_read_substate(api, &event)
1895    }
1896
1897    fn on_write_substate<Y: KernelInternalApi<System = Self>>(
1898        event: WriteSubstateEvent,
1899        api: &mut Y,
1900    ) -> Result<(), RuntimeError> {
1901        SystemModuleMixer::on_write_substate(api, &event)
1902    }
1903
1904    fn on_set_substate<Y: KernelInternalApi<System = Self>>(
1905        event: SetSubstateEvent,
1906        api: &mut Y,
1907    ) -> Result<(), RuntimeError> {
1908        SystemModuleMixer::on_set_substate(api, &event)
1909    }
1910
1911    fn on_remove_substate<Y: KernelInternalApi<System = Self>>(
1912        event: RemoveSubstateEvent,
1913        api: &mut Y,
1914    ) -> Result<(), RuntimeError> {
1915        SystemModuleMixer::on_remove_substate(api, &event)
1916    }
1917
1918    fn on_scan_keys<Y: KernelInternalApi<System = Self>>(
1919        event: ScanKeysEvent,
1920        api: &mut Y,
1921    ) -> Result<(), RuntimeError> {
1922        SystemModuleMixer::on_scan_keys(api, &event)
1923    }
1924
1925    fn on_drain_substates<Y: KernelInternalApi<System = Self>>(
1926        event: DrainSubstatesEvent,
1927        api: &mut Y,
1928    ) -> Result<(), RuntimeError> {
1929        SystemModuleMixer::on_drain_substates(api, &event)
1930    }
1931
1932    fn on_scan_sorted_substates<Y: KernelInternalApi<System = Self>>(
1933        event: ScanSortedSubstatesEvent,
1934        api: &mut Y,
1935    ) -> Result<(), RuntimeError> {
1936        SystemModuleMixer::on_scan_sorted_substates(api, &event)
1937    }
1938
1939    fn before_invoke<Y: KernelApi<CallbackObject = Self>>(
1940        invocation: &KernelInvocation<Actor>,
1941        api: &mut Y,
1942    ) -> Result<(), RuntimeError> {
1943        let is_to_barrier = invocation.call_frame_data.is_barrier();
1944        let destination_blueprint_id = invocation.call_frame_data.blueprint_id();
1945
1946        for node_id in invocation.args.owned_nodes() {
1947            Self::on_move_node(
1948                node_id,
1949                true,
1950                is_to_barrier,
1951                destination_blueprint_id.clone(),
1952                api,
1953            )?;
1954        }
1955
1956        SystemModuleMixer::before_invoke(api, invocation)
1957    }
1958
1959    fn on_execution_start<Y: KernelInternalApi<System = Self>>(
1960        api: &mut Y,
1961    ) -> Result<(), RuntimeError> {
1962        SystemModuleMixer::on_execution_start(api)
1963    }
1964
1965    fn invoke_upstream<Y: KernelApi<CallbackObject = Self>>(
1966        input: &IndexedScryptoValue,
1967        api: &mut Y,
1968    ) -> Result<IndexedScryptoValue, RuntimeError> {
1969        let mut system = SystemService::new(api);
1970        let actor = system.current_actor();
1971        let node_id = actor.node_id();
1972        let is_direct_access = actor.is_direct_access();
1973
1974        // Make dependent resources/components visible
1975        if let Some(blueprint_id) = actor.blueprint_id() {
1976            let key = BlueprintVersionKey {
1977                blueprint: blueprint_id.blueprint_name.clone(),
1978                version: BlueprintVersion::default(),
1979            };
1980
1981            let handle = system.kernel_open_substate_with_default(
1982                blueprint_id.package_address.as_node_id(),
1983                MAIN_BASE_PARTITION
1984                    .at_offset(PACKAGE_BLUEPRINT_DEPENDENCIES_PARTITION_OFFSET)
1985                    .unwrap(),
1986                &SubstateKey::Map(scrypto_encode(&key).unwrap()),
1987                LockFlags::read_only(),
1988                Some(|| {
1989                    let kv_entry = KeyValueEntrySubstate::<()>::default();
1990                    IndexedScryptoValue::from_typed(&kv_entry)
1991                }),
1992                SystemLockData::default(),
1993            )?;
1994            system.kernel_read_substate(handle)?;
1995            system.kernel_close_substate(handle)?;
1996        }
1997
1998        match &actor {
1999            Actor::Root => panic!("Root is invoked"),
2000            actor @ Actor::Method(MethodActor { ident, .. })
2001            | actor @ Actor::Function(FunctionActor { ident, .. }) => {
2002                let blueprint_id = actor.blueprint_id().unwrap();
2003
2004                //  Validate input
2005                let definition = system.load_blueprint_definition(
2006                    blueprint_id.package_address,
2007                    &BlueprintVersionKey::new_default(blueprint_id.blueprint_name.as_str()),
2008                )?;
2009
2010                let target = system.get_actor_type_target()?;
2011
2012                // Validate input
2013                system.validate_blueprint_payload(
2014                    &target,
2015                    BlueprintPayloadIdentifier::Function(ident.clone(), InputOrOutput::Input),
2016                    input.as_vec_ref(),
2017                )?;
2018
2019                // Validate receiver type
2020                let function_schema = definition
2021                    .interface
2022                    .functions
2023                    .get(ident)
2024                    .expect("Should exist due to schema check");
2025                match (&function_schema.receiver, node_id) {
2026                    (Some(receiver_info), Some(_)) => {
2027                        if is_direct_access
2028                            != receiver_info.ref_types.contains(RefTypes::DIRECT_ACCESS)
2029                        {
2030                            return Err(RuntimeError::SystemUpstreamError(
2031                                SystemUpstreamError::ReceiverNotMatch(ident.to_string()),
2032                            ));
2033                        }
2034                    }
2035                    (None, None) => {}
2036                    _ => {
2037                        return Err(RuntimeError::SystemUpstreamError(
2038                            SystemUpstreamError::ReceiverNotMatch(ident.to_string()),
2039                        ));
2040                    }
2041                }
2042
2043                // Execute
2044                let export = definition
2045                    .function_exports
2046                    .get(ident)
2047                    .expect("Schema should have validated this exists")
2048                    .clone();
2049                let output =
2050                    { V::invoke(&blueprint_id.package_address, export, input, &mut system)? };
2051
2052                // Validate output
2053                system.validate_blueprint_payload(
2054                    &target,
2055                    BlueprintPayloadIdentifier::Function(ident.clone(), InputOrOutput::Output),
2056                    output.as_vec_ref(),
2057                )?;
2058
2059                Ok(output)
2060            }
2061            Actor::BlueprintHook(BlueprintHookActor {
2062                blueprint_id, hook, ..
2063            }) => {
2064                // Find the export
2065                let definition = system.load_blueprint_definition(
2066                    blueprint_id.package_address,
2067                    &BlueprintVersionKey::new_default(blueprint_id.blueprint_name.as_str()),
2068                )?;
2069                let export =
2070                    definition
2071                        .hook_exports
2072                        .get(hook)
2073                        .ok_or(RuntimeError::SystemUpstreamError(
2074                            SystemUpstreamError::HookNotFound(*hook),
2075                        ))?;
2076
2077                // Input is not validated as they're created by system.
2078
2079                // Invoke the export
2080                let output = V::invoke(
2081                    &blueprint_id.package_address,
2082                    export.clone(),
2083                    input,
2084                    &mut system,
2085                )?;
2086
2087                // Check output against well-known schema
2088                match hook {
2089                    BlueprintHook::OnVirtualize => {
2090                        scrypto_decode::<OnVirtualizeOutput>(output.as_slice()).map(|_| ())
2091                    }
2092                    BlueprintHook::OnDrop => {
2093                        scrypto_decode::<OnDropOutput>(output.as_slice()).map(|_| ())
2094                    }
2095                    BlueprintHook::OnMove => {
2096                        scrypto_decode::<OnMoveOutput>(output.as_slice()).map(|_| ())
2097                    }
2098                }
2099                .map_err(|e| {
2100                    RuntimeError::SystemUpstreamError(SystemUpstreamError::OutputDecodeError(e))
2101                })?;
2102
2103                Ok(output)
2104            }
2105        }
2106    }
2107
2108    // Note: we check dangling nodes, in kernel, after auto-drop
2109    fn auto_drop<Y: KernelApi<CallbackObject = Self>>(
2110        nodes: Vec<NodeId>,
2111        api: &mut Y,
2112    ) -> Result<(), RuntimeError> {
2113        // Round 1 - drop all proofs
2114        for node_id in nodes {
2115            let type_info = TypeInfoBlueprint::get_type(&node_id, api)?;
2116
2117            if let TypeInfoSubstate::Object(ObjectInfo {
2118                blueprint_info: BlueprintInfo { blueprint_id, .. },
2119                ..
2120            }) = type_info
2121            {
2122                match (
2123                    blueprint_id.package_address,
2124                    blueprint_id.blueprint_name.as_str(),
2125                ) {
2126                    (RESOURCE_PACKAGE, FUNGIBLE_PROOF_BLUEPRINT) => {
2127                        let mut system = SystemService::new(api);
2128                        system.call_function(
2129                            RESOURCE_PACKAGE,
2130                            FUNGIBLE_PROOF_BLUEPRINT,
2131                            PROOF_DROP_IDENT,
2132                            scrypto_encode(&ProofDropInput {
2133                                proof: Proof(Own(node_id)),
2134                            })
2135                            .unwrap(),
2136                        )?;
2137                    }
2138                    (RESOURCE_PACKAGE, NON_FUNGIBLE_PROOF_BLUEPRINT) => {
2139                        let mut system = SystemService::new(api);
2140                        system.call_function(
2141                            RESOURCE_PACKAGE,
2142                            NON_FUNGIBLE_PROOF_BLUEPRINT,
2143                            PROOF_DROP_IDENT,
2144                            scrypto_encode(&ProofDropInput {
2145                                proof: Proof(Own(node_id)),
2146                            })
2147                            .unwrap(),
2148                        )?;
2149                    }
2150                    _ => {
2151                        // no-op
2152                    }
2153                }
2154            }
2155        }
2156
2157        Ok(())
2158    }
2159
2160    fn on_execution_finish<Y: KernelInternalApi<System = Self>>(
2161        message: &CallFrameMessage,
2162        api: &mut Y,
2163    ) -> Result<(), RuntimeError> {
2164        SystemModuleMixer::on_execution_finish(api, message)?;
2165
2166        Ok(())
2167    }
2168
2169    fn on_get_stack_id<Y: KernelInternalApi<System = Self>>(
2170        api: &mut Y,
2171    ) -> Result<(), RuntimeError> {
2172        SystemModuleMixer::on_get_stack_id(api)
2173    }
2174
2175    fn on_switch_stack<Y: KernelInternalApi<System = Self>>(
2176        api: &mut Y,
2177    ) -> Result<(), RuntimeError> {
2178        SystemModuleMixer::on_switch_stack(api)
2179    }
2180
2181    fn on_send_to_stack<Y: KernelInternalApi<System = Self>>(
2182        value: &IndexedScryptoValue,
2183        api: &mut Y,
2184    ) -> Result<(), RuntimeError> {
2185        SystemModuleMixer::on_send_to_stack(api, value.len())
2186    }
2187
2188    fn on_set_call_frame_data<Y: KernelInternalApi<System = Self>>(
2189        data: &Self::CallFrameData,
2190        api: &mut Y,
2191    ) -> Result<(), RuntimeError> {
2192        SystemModuleMixer::on_set_call_frame_data(api, data.len())
2193    }
2194
2195    fn on_get_owned_nodes<Y: KernelInternalApi<System = Self>>(
2196        api: &mut Y,
2197    ) -> Result<(), RuntimeError> {
2198        SystemModuleMixer::on_get_owned_nodes(api)
2199    }
2200
2201    //--------------------------------------------------------------------------
2202    // Note that the following logic doesn't go through mixer and is not costed
2203    //--------------------------------------------------------------------------
2204
2205    fn after_invoke<Y: KernelApi<CallbackObject = Self>>(
2206        output: &IndexedScryptoValue,
2207        api: &mut Y,
2208    ) -> Result<(), RuntimeError> {
2209        let current_actor = api.kernel_get_system_state().current_call_frame;
2210        let is_to_barrier = current_actor.is_barrier();
2211        let destination_blueprint_id = current_actor.blueprint_id();
2212        for node_id in output.owned_nodes() {
2213            Self::on_move_node(
2214                node_id,
2215                false,
2216                is_to_barrier,
2217                destination_blueprint_id.clone(),
2218                api,
2219            )?;
2220        }
2221
2222        SystemModuleMixer::after_invoke(api, output)
2223    }
2224
2225    fn on_allocate_node_id<Y: KernelInternalApi<System = Self>>(
2226        entity_type: EntityType,
2227        api: &mut Y,
2228    ) -> Result<(), RuntimeError> {
2229        SystemModuleMixer::on_allocate_node_id(api, entity_type)
2230    }
2231
2232    fn on_mark_substate_as_transient<Y: KernelInternalApi<System = Self>>(
2233        node_id: &NodeId,
2234        partition_number: &PartitionNumber,
2235        substate_key: &SubstateKey,
2236        api: &mut Y,
2237    ) -> Result<(), RuntimeError> {
2238        SystemModuleMixer::on_mark_substate_as_transient(
2239            api,
2240            node_id,
2241            partition_number,
2242            substate_key,
2243        )
2244    }
2245
2246    fn on_substate_lock_fault<Y: KernelApi<CallbackObject = Self>>(
2247        node_id: NodeId,
2248        partition_num: PartitionNumber,
2249        offset: &SubstateKey,
2250        api: &mut Y,
2251    ) -> Result<bool, RuntimeError> {
2252        // As currently implemented, this should always be called with partition_num=0 and offset=0
2253        // since all nodes are access by accessing their type info first
2254        // This check is simply a sanity check that this invariant remain true
2255        if !partition_num.eq(&TYPE_INFO_FIELD_PARTITION)
2256            || !offset.eq(&TypeInfoField::TypeInfo.into())
2257        {
2258            return Ok(false);
2259        }
2260
2261        let (blueprint_id, variant_id) = match node_id.entity_type() {
2262            Some(EntityType::GlobalPreallocatedSecp256k1Account) => (
2263                BlueprintId::new(&ACCOUNT_PACKAGE, ACCOUNT_BLUEPRINT),
2264                ACCOUNT_CREATE_PREALLOCATED_SECP256K1_ID,
2265            ),
2266            Some(EntityType::GlobalPreallocatedEd25519Account) => (
2267                BlueprintId::new(&ACCOUNT_PACKAGE, ACCOUNT_BLUEPRINT),
2268                ACCOUNT_CREATE_PREALLOCATED_ED25519_ID,
2269            ),
2270            Some(EntityType::GlobalPreallocatedSecp256k1Identity) => (
2271                BlueprintId::new(&IDENTITY_PACKAGE, IDENTITY_BLUEPRINT),
2272                IDENTITY_CREATE_PREALLOCATED_SECP256K1_ID,
2273            ),
2274            Some(EntityType::GlobalPreallocatedEd25519Identity) => (
2275                BlueprintId::new(&IDENTITY_PACKAGE, IDENTITY_BLUEPRINT),
2276                IDENTITY_CREATE_PREALLOCATED_ED25519_ID,
2277            ),
2278            _ => return Ok(false),
2279        };
2280
2281        let mut service = SystemService::new(api);
2282        let definition = service.load_blueprint_definition(
2283            blueprint_id.package_address,
2284            &BlueprintVersionKey {
2285                blueprint: blueprint_id.blueprint_name.clone(),
2286                version: BlueprintVersion::default(),
2287            },
2288        )?;
2289        if definition
2290            .hook_exports
2291            .contains_key(&BlueprintHook::OnVirtualize)
2292        {
2293            let mut system = SystemService::new(api);
2294            let address = GlobalAddress::new_or_panic(node_id.into());
2295            let address_reservation =
2296                system.allocate_virtual_global_address(blueprint_id.clone(), address)?;
2297
2298            api.kernel_invoke(Box::new(KernelInvocation {
2299                call_frame_data: Actor::BlueprintHook(BlueprintHookActor {
2300                    blueprint_id: blueprint_id.clone(),
2301                    hook: BlueprintHook::OnVirtualize,
2302                    receiver: None,
2303                }),
2304                args: IndexedScryptoValue::from_typed(&OnVirtualizeInput {
2305                    variant_id,
2306                    rid: copy_u8_array(&node_id.as_bytes()[1..]),
2307                    address_reservation,
2308                }),
2309            }))?;
2310            Ok(true)
2311        } else {
2312            Ok(false)
2313        }
2314    }
2315
2316    fn on_drop_node_mut<Y: KernelApi<CallbackObject = Self>>(
2317        node_id: &NodeId,
2318        api: &mut Y,
2319    ) -> Result<(), RuntimeError> {
2320        let type_info = TypeInfoBlueprint::get_type(node_id, api)?;
2321
2322        match type_info {
2323            TypeInfoSubstate::Object(node_object_info) => {
2324                let mut service = SystemService::new(api);
2325                let definition = service.load_blueprint_definition(
2326                    node_object_info.blueprint_info.blueprint_id.package_address,
2327                    &BlueprintVersionKey {
2328                        blueprint: node_object_info
2329                            .blueprint_info
2330                            .blueprint_id
2331                            .blueprint_name
2332                            .clone(),
2333                        version: BlueprintVersion::default(),
2334                    },
2335                )?;
2336                if definition.hook_exports.contains_key(&BlueprintHook::OnDrop) {
2337                    api.kernel_invoke(Box::new(KernelInvocation {
2338                        call_frame_data: Actor::BlueprintHook(BlueprintHookActor {
2339                            blueprint_id: node_object_info.blueprint_info.blueprint_id.clone(),
2340                            hook: BlueprintHook::OnDrop,
2341                            receiver: Some(*node_id),
2342                        }),
2343                        args: IndexedScryptoValue::from_typed(&OnDropInput {}),
2344                    }))
2345                    .map(|_| ())
2346                } else {
2347                    Ok(())
2348                }
2349            }
2350            TypeInfoSubstate::KeyValueStore(_)
2351            | TypeInfoSubstate::GlobalAddressReservation(_)
2352            | TypeInfoSubstate::GlobalAddressPhantom(_) => {
2353                // There is no way to drop a non-object through system API, triggering `NotAnObject` error.
2354                Ok(())
2355            }
2356        }
2357    }
2358}