Skip to main content

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