radix_engine/system/
system_callback.rs

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