radix_engine/
errors.rs

1use crate::blueprints::access_controller::AccessControllerError;
2use crate::blueprints::account::AccountError;
3use crate::blueprints::consensus_manager::{ConsensusManagerError, ValidatorError};
4use crate::blueprints::package::PackageError;
5use crate::blueprints::pool::v1::errors::{
6    multi_resource_pool::Error as MultiResourcePoolError,
7    one_resource_pool::Error as OneResourcePoolError,
8    two_resource_pool::Error as TwoResourcePoolError,
9};
10use crate::blueprints::resource::{AuthZoneError, NonFungibleVaultError};
11use crate::blueprints::resource::{
12    BucketError, FungibleResourceManagerError, NonFungibleResourceManagerError, ProofError,
13    VaultError, WorktopError,
14};
15use crate::blueprints::transaction_processor::TransactionProcessorError;
16use crate::internal_prelude::*;
17use crate::kernel::call_frame::{
18    CallFrameDrainSubstatesError, CallFrameRemoveSubstateError, CallFrameScanKeysError,
19    CallFrameScanSortedSubstatesError, CallFrameSetSubstateError, CloseSubstateError,
20    CreateFrameError, CreateNodeError, DropNodeError, MarkTransientSubstateError,
21    MovePartitionError, OpenSubstateError, PassMessageError, PinNodeError, ReadSubstateError,
22    WriteSubstateError,
23};
24use crate::object_modules::metadata::MetadataError;
25use crate::object_modules::role_assignment::RoleAssignmentError;
26use crate::object_modules::royalty::ComponentRoyaltyError;
27use crate::system::system_modules::auth::AuthError;
28use crate::system::system_modules::costing::CostingError;
29use crate::system::system_modules::limits::TransactionLimitsError;
30use crate::system::system_type_checker::TypeCheckError;
31use crate::transaction::AbortReason;
32use crate::vm::wasm::WasmRuntimeError;
33use crate::vm::ScryptoVmVersionError;
34use radix_engine_interface::api::object_api::ModuleId;
35use radix_engine_interface::api::{ActorStateHandle, AttachedModuleId};
36use radix_engine_interface::blueprints::package::{BlueprintPartitionType, CanonicalBlueprintId};
37use radix_transactions::model::IntentHash;
38use sbor::representations::PrintMode;
39
40#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
41pub enum IdAllocationError {
42    OutOfID,
43}
44
45pub trait CanBeAbortion {
46    fn abortion(&self) -> Option<&AbortReason>;
47}
48
49pub mod error_models {
50    use radix_common::prelude::*;
51
52    /// This is a special NodeId which gets encoded as a reference in SBOR...
53    /// This means that it can be rendered as a string in the output.
54    #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, ScryptoSbor)]
55    #[sbor(
56        as_type = "Reference",
57        as_ref = "&Reference(self.0)",
58        from_value = "Self(value.0)",
59        type_name = "NodeId"
60    )]
61    pub struct ReferencedNodeId(pub radix_common::prelude::NodeId);
62
63    impl Debug for ReferencedNodeId {
64        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65            self.0.fmt(f)
66        }
67    }
68
69    impl From<radix_common::prelude::NodeId> for ReferencedNodeId {
70        fn from(value: radix_common::prelude::NodeId) -> Self {
71            Self(value)
72        }
73    }
74
75    /// This is a special NodeId which gets encoded as a reference in SBOR...
76    /// This means that it can be rendered as a string in the output.
77    #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, ScryptoSbor)]
78    #[sbor(
79        as_type = "Own",
80        as_ref = "&Own(self.0)",
81        from_value = "Self(value.0)",
82        type_name = "NodeId"
83    )]
84    pub struct OwnedNodeId(pub radix_common::prelude::NodeId);
85
86    impl Debug for OwnedNodeId {
87        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88            self.0.fmt(f)
89        }
90    }
91
92    impl From<radix_common::prelude::NodeId> for OwnedNodeId {
93        fn from(value: radix_common::prelude::NodeId) -> Self {
94            Self(value)
95        }
96    }
97}
98
99lazy_static::lazy_static! {
100    /// See [`HISTORIC_RUNTIME_ERROR_SCHEMAS`] for more information.
101    ///
102    /// Although the RejectionReason isn't used on the node, we do a similar thing anyway.
103    static ref HISTORIC_REJECTION_REASON_SCHEMAS: [ScryptoSingleTypeSchema; 1] = {
104        [
105            ScryptoSingleTypeSchema::from(include_bytes!("rejection_reason_cuttlefish_schema.bin")),
106        ]
107    };
108}
109
110/// Represents an error which causes a transaction to be rejected.
111#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
112// #[derive(ScryptoSborAssertion)]
113// #[sbor_assert(fixed("FILE:rejection_reason_[NEW-VERSION-NAME]_schema.bin"), generate)]
114// #[sbor_assert(fixed("FILE:rejection_reason_[UNDEPLOYED-CURRENT-VERSION-NAME]_schema.bin"), regenerate)]
115pub enum RejectionReason {
116    TransactionEpochNotYetValid {
117        /// `start_epoch_inclusive`
118        valid_from: Epoch,
119        current_epoch: Epoch,
120    },
121    TransactionEpochNoLongerValid {
122        /// One epoch before `end_epoch_exclusive`
123        valid_until: Epoch,
124        current_epoch: Epoch,
125    },
126    TransactionProposerTimestampNotYetValid {
127        valid_from_inclusive: Instant,
128        current_time: Instant,
129    },
130    TransactionProposerTimestampNoLongerValid {
131        valid_to_exclusive: Instant,
132        current_time: Instant,
133    },
134    IntentHashPreviouslyCommitted(IntentHash),
135    IntentHashPreviouslyCancelled(IntentHash),
136
137    BootloadingError(BootloadingError),
138
139    ErrorBeforeLoanAndDeferredCostsRepaid(RuntimeError),
140    SuccessButFeeLoanNotRepaid,
141    SubintentsNotYetSupported,
142}
143
144impl<'a> ContextualDisplay<ScryptoValueDisplayContext<'a>> for RejectionReason {
145    type Error = fmt::Error;
146
147    fn contextual_format<F: fmt::Write>(
148        &self,
149        f: &mut F,
150        context: &ScryptoValueDisplayContext,
151    ) -> Result<(), Self::Error> {
152        self.create_persistable().contextual_format(f, context)
153    }
154}
155
156impl RejectionReason {
157    pub fn create_persistable(&self) -> PersistableRejectionReason {
158        PersistableRejectionReason {
159            schema_index: HISTORIC_REJECTION_REASON_SCHEMAS.len() as u32 - 1,
160            encoded_rejection_reason: scrypto_decode(&scrypto_encode(self).unwrap()).unwrap(),
161        }
162    }
163}
164
165#[derive(Debug, Clone, ScryptoSbor)]
166pub struct PersistableRejectionReason {
167    pub schema_index: u32,
168    pub encoded_rejection_reason: ScryptoOwnedRawValue,
169}
170
171impl<'a> ContextualDisplay<ScryptoValueDisplayContext<'a>> for PersistableRejectionReason {
172    type Error = fmt::Error;
173
174    /// See [`SerializableRuntimeError::contextual_format`] for more information.
175    fn contextual_format<F: fmt::Write>(
176        &self,
177        f: &mut F,
178        context: &ScryptoValueDisplayContext,
179    ) -> Result<(), Self::Error> {
180        let value = &self.encoded_rejection_reason;
181        let formatted_optional = HISTORIC_REJECTION_REASON_SCHEMAS
182            .get(self.schema_index as usize)
183            .and_then(|schema| {
184                format_debug_like_value(
185                    f,
186                    schema,
187                    value,
188                    sbor::representations::PrintMode::SingleLine,
189                    *context,
190                )
191            });
192        match formatted_optional {
193            Some(result) => result,
194            None => match scrypto_encode(&value) {
195                Ok(encoded) => write!(f, "UnknownRejectionReason({})", hex::encode(encoded)),
196                Err(error) => write!(f, "CannotDisplayRejectionReason({error:?})"),
197            },
198        }
199    }
200}
201
202fn format_debug_like_value(
203    f: &mut impl fmt::Write,
204    schema: &SingleTypeSchema<ScryptoCustomSchema>,
205    value: &ScryptoRawValue,
206    print_mode: PrintMode,
207    custom_context: ScryptoValueDisplayContext,
208) -> Option<fmt::Result> {
209    use sbor::representations::*;
210    let type_id = schema.type_id;
211    let schema = schema.schema.as_unique_version();
212    let depth_limit = SCRYPTO_SBOR_V1_MAX_DEPTH;
213
214    // Sanity check this is the correct schema...
215    validate_partial_payload_against_schema::<ScryptoCustomExtension, _>(
216        value.value_body_bytes(),
217        traversal::ExpectedStart::ValueBody(value.value_kind()),
218        true,
219        0,
220        schema,
221        type_id,
222        &(),
223        depth_limit,
224    )
225    .ok()?;
226
227    // Then encode it...
228    let display_parameters = ValueDisplayParameters::Annotated {
229        display_mode: DisplayMode::RustLike(RustLikeOptions::debug_like()),
230        print_mode,
231        custom_context,
232        schema,
233        type_id,
234        depth_limit,
235    };
236
237    Some(write!(f, "{}", value.display(display_parameters)))
238}
239
240impl From<BootloadingError> for RejectionReason {
241    fn from(value: BootloadingError) -> Self {
242        RejectionReason::BootloadingError(value)
243    }
244}
245
246#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
247pub enum TransactionExecutionError {
248    /// An error ocurred when bootloading a kernel.
249    BootloadingError(BootloadingError),
250
251    /// A runtime error
252    RuntimeError(RuntimeError),
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
256pub enum BootloadingError {
257    ReferencedNodeDoesNotExist(error_models::ReferencedNodeId),
258    ReferencedNodeIsNotAnObject(error_models::ReferencedNodeId),
259    ReferencedNodeDoesNotAllowDirectAccess(error_models::ReferencedNodeId),
260
261    FailedToApplyDeferredCosts(CostingError),
262}
263
264lazy_static::lazy_static! {
265    /// This list is used to render string messages from historically stored
266    /// [`SerializableRuntimeError`]s in the LocalTransactionExecution index in the node.
267    ///
268    /// In particular, each [`SerializableRuntimeError`] stores the index of the current schema
269    /// in this array at the time it was created.
270    ///
271    /// But of course, when the error is read (e.g. in the Core API stream 10 months later),
272    /// we may be a few protocol versions down the line, and the `RuntimeError` schema may have changed.
273    ///
274    /// To get around this, we simply use the stored schema index to look up the correct schema here.
275    /// And we use this historic schema to render the error message.
276    ///
277    /// This allows us the following benefits:
278    /// * We can use a condensed error encoding (rather than just storing it as a string)
279    /// * We can change the `RuntimeError` schema freely, as long as we ensure old schemas are kept here.
280    ///   Tests will ensure we don't break this.
281    ///
282    /// We MUST NOT change/remove/reorder existing schemas in this list, if they have been released
283    /// in a node version. This is to ensure that we can always decode old errors.
284    ///
285    /// New schemas can be generated with `#[sbor_assert(fixed("FILE:xxx"))]` generator above.
286    static ref HISTORIC_RUNTIME_ERROR_SCHEMAS: [ScryptoSingleTypeSchema; 2] = {
287        [
288            ScryptoSingleTypeSchema::from(include_bytes!("runtime_error_pre_cuttlefish_schema.bin")),
289            ScryptoSingleTypeSchema::from(include_bytes!("runtime_error_cuttlefish_schema.bin")),
290        ]
291    };
292}
293
294/// Represents an error when executing a transaction.
295#[derive(Clone, PartialEq, Eq, ScryptoSbor, Debug)]
296// You are welcome to update the RuntimeError structure, but the tests will make you ensure
297// that the current schema is the last in HISTORIC_RUNTIME_ERROR_SCHEMAS above, and that
298// any schema used in a released node version never gets removed.
299//
300// What this means is:
301// - You may regenerate the schema for the current version, if it's never been released.
302// - Otherwise, you will want to generate a new schema for the new version.
303//
304// So:
305// - Temporarily uncomment the derive, and one of the sbor_assert lines below
306// - Rename the file in the line
307// - Run the test to (re)generate the schema
308// - Revert the changes to these few lines
309// - If it's a new schema, add it to the HISTORIC_RUNTIME_ERROR_SCHEMAS list above.
310// - Check the `the_current_runtime_schema_is_last_on_historic_runtime_list` test passes.
311//
312// #[derive(ScryptoSborAssertion)]
313// #[sbor_assert(fixed("FILE:runtime_error_[NEW-VERSION-NAME]_schema.bin"), generate)]
314// #[sbor_assert(fixed("FILE:runtime_error_[UNDEPLOYED-CURRENT-VERSION-NAME]_schema.bin"), regenerate)]
315pub enum RuntimeError {
316    /// An error occurred within the kernel.
317    KernelError(KernelError),
318
319    /// An error occurred within the system, notably the SystemAPI implementation.
320    SystemError(SystemError),
321
322    /// An error occurred within a specific system module, like auth, costing and royalty.
323    /// TODO: merge into SystemError?
324    SystemModuleError(SystemModuleError),
325
326    /// An error issued by the system when invoking upstream (such as blueprints, node modules).
327    /// TODO: merge into SystemError?
328    SystemUpstreamError(SystemUpstreamError),
329
330    /// An error occurred in the vm layer
331    VmError(VmError),
332
333    /// An error occurred within application logic, like the RE models.
334    ApplicationError(ApplicationError),
335
336    FinalizationCostingError(CostingError),
337}
338
339impl<'a> ContextualDisplay<ScryptoValueDisplayContext<'a>> for RuntimeError {
340    type Error = fmt::Error;
341
342    fn contextual_format<F: fmt::Write>(
343        &self,
344        f: &mut F,
345        context: &ScryptoValueDisplayContext,
346    ) -> Result<(), Self::Error> {
347        self.create_persistable().contextual_format(f, context)
348    }
349}
350
351impl RuntimeError {
352    pub fn create_persistable(&self) -> PersistableRuntimeError {
353        PersistableRuntimeError {
354            schema_index: HISTORIC_RUNTIME_ERROR_SCHEMAS.len() as u32 - 1,
355            encoded_error: scrypto_decode(&scrypto_encode(self).unwrap()).unwrap(),
356        }
357    }
358}
359
360#[derive(Debug, Clone, ScryptoSbor)]
361pub struct PersistableRuntimeError {
362    pub schema_index: u32,
363    // RawValue and RawPayload will change in https://github.com/radixdlt/radixdlt-scrypto/pull/1860
364    // It's important we stick with `RawValue` here (so it encodes/decode as SBOR itself),
365    // but ideally it would be a full payload underneath. This can be the case from #1860.
366    pub encoded_error: ScryptoOwnedRawValue,
367}
368
369/// This is used to render the error message, with a fallback if an invalid schema
370/// is associated with the error.
371///
372/// This fallback is necessary due to historic breakages of backwards compatibility
373/// in the `RuntimeError` type structure.
374///
375/// Specifically, at anemone / bottlenose, there were very minor changes, which affected
376/// a tiny minority of errors. If we could find the historic schemas, we could actually
377/// render them properly here. Unfortunately, the historic schemas are not easy to find out
378/// (it would require backporting the schema generation logic), so instead we just have
379/// a fallback for these cases.
380///
381/// This fallback will only be applied on nodes, when returning occasional errors for
382/// old transactions that haven't resynced since Bottlenose.
383impl<'a> ContextualDisplay<ScryptoValueDisplayContext<'a>> for PersistableRuntimeError {
384    type Error = fmt::Error;
385
386    fn contextual_format<F: fmt::Write>(
387        &self,
388        f: &mut F,
389        context: &ScryptoValueDisplayContext,
390    ) -> Result<(), Self::Error> {
391        let value = &self.encoded_error;
392        let formatted_optional = HISTORIC_RUNTIME_ERROR_SCHEMAS
393            .get(self.schema_index as usize)
394            .and_then(|schema| {
395                format_debug_like_value(f, schema, value, PrintMode::SingleLine, *context)
396            });
397        match formatted_optional {
398            Some(result) => result,
399            None => match scrypto_encode(&value) {
400                Ok(encoded) => write!(f, "UnknownError({})", hex::encode(encoded)),
401                Err(error) => write!(f, "CannotDisplayError({error:?})"),
402            },
403        }
404    }
405}
406
407impl SystemApiError for RuntimeError {}
408
409impl From<KernelError> for RuntimeError {
410    fn from(error: KernelError) -> Self {
411        RuntimeError::KernelError(error.into())
412    }
413}
414
415impl From<SystemUpstreamError> for RuntimeError {
416    fn from(error: SystemUpstreamError) -> Self {
417        RuntimeError::SystemUpstreamError(error.into())
418    }
419}
420
421impl From<SystemModuleError> for RuntimeError {
422    fn from(error: SystemModuleError) -> Self {
423        RuntimeError::SystemModuleError(error.into())
424    }
425}
426
427impl From<ApplicationError> for RuntimeError {
428    fn from(error: ApplicationError) -> Self {
429        RuntimeError::ApplicationError(error.into())
430    }
431}
432
433impl CanBeAbortion for RuntimeError {
434    fn abortion(&self) -> Option<&AbortReason> {
435        match self {
436            RuntimeError::KernelError(_) => None,
437            RuntimeError::VmError(_) => None,
438            RuntimeError::SystemError(_) => None,
439            RuntimeError::SystemUpstreamError(_) => None,
440            RuntimeError::SystemModuleError(err) => err.abortion(),
441            RuntimeError::ApplicationError(_) => None,
442            RuntimeError::FinalizationCostingError(_) => None,
443        }
444    }
445}
446
447#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
448pub enum KernelError {
449    // Call frame
450    CallFrameError(CallFrameError),
451
452    // ID allocation
453    IdAllocationError(IdAllocationError),
454
455    // Substate lock/read/write/unlock
456    SubstateHandleDoesNotExist(SubstateHandle),
457
458    OrphanedNodes(Vec<error_models::OwnedNodeId>),
459
460    StackError(StackError),
461}
462
463#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
464pub struct InvalidDropAccess {
465    pub node_id: error_models::ReferencedNodeId,
466    pub package_address: PackageAddress,
467    pub blueprint_name: String,
468    pub actor_package: Option<PackageAddress>,
469}
470
471#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
472pub struct InvalidGlobalizeAccess {
473    pub package_address: PackageAddress,
474    pub blueprint_name: String,
475    pub actor_package: Option<PackageAddress>,
476}
477
478impl CanBeAbortion for VmError {
479    fn abortion(&self) -> Option<&AbortReason> {
480        match self {
481            VmError::Wasm(err) => err.abortion(),
482            _ => None,
483        }
484    }
485}
486
487impl From<CallFrameError> for KernelError {
488    fn from(value: CallFrameError) -> Self {
489        KernelError::CallFrameError(value)
490    }
491}
492
493#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
494pub enum CallFrameError {
495    CreateFrameError(CreateFrameError),
496    PassMessageError(PassMessageError),
497
498    CreateNodeError(CreateNodeError),
499    DropNodeError(DropNodeError),
500    PinNodeError(PinNodeError),
501
502    MovePartitionError(MovePartitionError),
503
504    MarkTransientSubstateError(MarkTransientSubstateError),
505    OpenSubstateError(OpenSubstateError),
506    CloseSubstateError(CloseSubstateError),
507    ReadSubstateError(ReadSubstateError),
508    WriteSubstateError(WriteSubstateError),
509
510    ScanSubstatesError(CallFrameScanKeysError),
511    DrainSubstatesError(CallFrameDrainSubstatesError),
512    ScanSortedSubstatesError(CallFrameScanSortedSubstatesError),
513    SetSubstatesError(CallFrameSetSubstateError),
514    RemoveSubstatesError(CallFrameRemoveSubstateError),
515}
516
517#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
518pub enum StackError {
519    InvalidStackId,
520}
521
522#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
523pub enum SystemError {
524    NoBlueprintId,
525    NoPackageAddress,
526    InvalidActorStateHandle,
527    InvalidActorRefHandle,
528
529    GlobalizingTransientBlueprint,
530    GlobalAddressDoesNotExist,
531    NotAnAddressReservation,
532    NotAnObject,
533    NotAKeyValueStore,
534    ModulesDontHaveOuterObjects,
535    ActorNodeIdDoesNotExist,
536    OuterObjectDoesNotExist,
537    NotAFieldHandle,
538    NotAFieldWriteHandle,
539    RootHasNoType,
540    AddressBech32EncodeError,
541    TypeCheckError(TypeCheckError),
542    FieldDoesNotExist(BlueprintId, u8),
543    CollectionIndexDoesNotExist(BlueprintId, u8),
544    CollectionIndexIsOfWrongType(
545        BlueprintId,
546        u8,
547        BlueprintPartitionType,
548        BlueprintPartitionType,
549    ),
550    KeyValueEntryLocked,
551    FieldLocked(ActorStateHandle, u8),
552    ObjectModuleDoesNotExist(AttachedModuleId),
553    NotAKeyValueEntryHandle,
554    NotAKeyValueEntryWriteHandle,
555    InvalidLockFlags,
556    CannotGlobalize(CannotGlobalizeError),
557    MissingModule(ModuleId),
558    InvalidGlobalAddressReservation,
559    InvalidChildObjectCreation,
560    InvalidModuleType(Box<InvalidModuleType>),
561    CreateObjectError(Box<CreateObjectError>),
562    InvalidGenericArgs,
563    InvalidFeature(String),
564    AssertAccessRuleFailed,
565    BlueprintDoesNotExist(CanonicalBlueprintId),
566    AuthTemplateDoesNotExist(CanonicalBlueprintId),
567    InvalidGlobalizeAccess(Box<InvalidGlobalizeAccess>),
568    InvalidDropAccess(Box<InvalidDropAccess>),
569    CostingModuleNotEnabled,
570    AuthModuleNotEnabled,
571    TransactionRuntimeModuleNotEnabled,
572    ForceWriteEventFlagsNotAllowed,
573
574    BlueprintTypeNotFound(String),
575
576    BlsError(String),
577    InputDataEmpty,
578
579    /// A panic that's occurred in the system-layer or below. We're calling it system panic since
580    /// we're treating the system as a black-box here.
581    ///
582    /// Note that this is only used when feature std is used.
583    SystemPanic(String),
584
585    CannotLockFeeInChildSubintent(usize),
586    IntentError(IntentError),
587}
588
589#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
590pub enum IntentError {
591    CannotVerifyParentOnRoot,
592    CannotYieldProof,
593    VerifyParentFailed,
594    InvalidIntentIndex(usize),
595    NoParentToYieldTo,
596    AssertNextCallReturnsFailed(ResourceConstraintsError),
597    AssertBucketContentsFailed(ResourceConstraintError),
598}
599
600#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
601pub enum EventError {
602    SchemaNotFoundError {
603        blueprint: BlueprintId,
604        event_name: String,
605    },
606    EventSchemaNotMatch(String),
607    NoAssociatedPackage,
608    InvalidActor,
609}
610
611#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
612pub enum SystemUpstreamError {
613    SystemFunctionCallNotAllowed,
614
615    FnNotFound(String),
616    ReceiverNotMatch(String),
617    HookNotFound(BlueprintHook),
618
619    InputDecodeError(DecodeError),
620    InputSchemaNotMatch(String, String),
621
622    OutputDecodeError(DecodeError),
623    OutputSchemaNotMatch(String, String),
624}
625
626#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
627pub enum VmError {
628    Native(NativeRuntimeError),
629    Wasm(WasmRuntimeError),
630    ScryptoVmVersion(ScryptoVmVersionError),
631}
632
633#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
634pub enum NativeRuntimeError {
635    InvalidCodeId,
636
637    /// A panic was encountered in Native code.
638    Trap {
639        export_name: String,
640        input: ScryptoValue,
641        error: String,
642    },
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
646pub enum CreateObjectError {
647    BlueprintNotFound(String),
648    InvalidFieldDueToFeature(BlueprintId, u8),
649    MissingField(BlueprintId, u8),
650    InvalidFieldIndex(BlueprintId, u8),
651    SchemaValidationError(BlueprintId, String),
652    InvalidSubstateWrite(String),
653}
654
655#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
656pub enum SystemModuleError {
657    AuthError(AuthError),
658    CostingError(CostingError),
659    TransactionLimitsError(TransactionLimitsError),
660    EventError(Box<EventError>),
661}
662
663#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
664pub struct InvalidModuleType {
665    pub expected_blueprint: BlueprintId,
666    pub actual_blueprint: BlueprintId,
667}
668
669#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
670pub enum CannotGlobalizeError {
671    NotAnObject,
672    AlreadyGlobalized,
673    InvalidBlueprintId,
674}
675
676impl CanBeAbortion for SystemModuleError {
677    fn abortion(&self) -> Option<&AbortReason> {
678        match self {
679            Self::CostingError(err) => err.abortion(),
680            _ => None,
681        }
682    }
683}
684
685impl From<AuthError> for SystemModuleError {
686    fn from(error: AuthError) -> Self {
687        Self::AuthError(error)
688    }
689}
690
691impl From<CostingError> for SystemModuleError {
692    fn from(error: CostingError) -> Self {
693        Self::CostingError(error)
694    }
695}
696
697/// This enum is to help with designing intuitive error abstractions.
698/// Each engine module can have its own [`SelfError`], but can also wrap arbitrary downstream errors.
699/// Ultimately these errors get flattened out to a [`RuntimeError`] anyway.
700#[derive(Debug, Clone)]
701pub enum InvokeError<E: SelfError> {
702    SelfError(E),
703    Downstream(RuntimeError),
704}
705
706/// This is a trait for the non-Downstream part of [`InvokeError`]
707/// We can't use `Into<RuntimeError>` because we need [`RuntimeError`] _not_ to implement it.
708pub trait SelfError {
709    fn into_runtime_error(self) -> RuntimeError;
710}
711
712impl<E: Into<ApplicationError>> SelfError for E {
713    fn into_runtime_error(self) -> RuntimeError {
714        self.into().into()
715    }
716}
717
718impl<E: SelfError> From<RuntimeError> for InvokeError<E> {
719    fn from(runtime_error: RuntimeError) -> Self {
720        InvokeError::Downstream(runtime_error)
721    }
722}
723
724impl<E: SelfError> From<E> for InvokeError<E> {
725    fn from(error: E) -> Self {
726        InvokeError::SelfError(error)
727    }
728}
729
730impl<E: SelfError> InvokeError<E> {
731    pub fn error(error: E) -> Self {
732        InvokeError::SelfError(error)
733    }
734
735    pub fn downstream(runtime_error: RuntimeError) -> Self {
736        InvokeError::Downstream(runtime_error)
737    }
738}
739
740impl<E: SelfError> From<InvokeError<E>> for RuntimeError {
741    fn from(error: InvokeError<E>) -> Self {
742        match error {
743            InvokeError::Downstream(runtime_error) => runtime_error,
744            InvokeError::SelfError(e) => e.into_runtime_error(),
745        }
746    }
747}
748
749#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
750pub enum ApplicationError {
751    //===================
752    // General errors
753    //===================
754    // TODO: this should never happen because of schema check?
755    ExportDoesNotExist(String),
756
757    // TODO: this should never happen because of schema check?
758    InputDecodeError(DecodeError),
759
760    /// A panic.
761    PanicMessage(String),
762
763    //===================
764    // Node module errors
765    //===================
766    RoleAssignmentError(RoleAssignmentError),
767
768    MetadataError(MetadataError),
769
770    ComponentRoyaltyError(ComponentRoyaltyError),
771
772    //===================
773    // Blueprint errors
774    //===================
775    TransactionProcessorError(TransactionProcessorError),
776
777    PackageError(PackageError),
778
779    ConsensusManagerError(ConsensusManagerError),
780
781    ValidatorError(ValidatorError),
782
783    FungibleResourceManagerError(FungibleResourceManagerError),
784
785    NonFungibleResourceManagerError(NonFungibleResourceManagerError),
786
787    BucketError(BucketError),
788
789    ProofError(ProofError),
790
791    NonFungibleVaultError(NonFungibleVaultError),
792
793    VaultError(VaultError),
794
795    WorktopError(WorktopError),
796
797    AuthZoneError(AuthZoneError),
798
799    AccountError(AccountError),
800
801    AccessControllerError(AccessControllerError),
802
803    OneResourcePoolError(OneResourcePoolError),
804
805    TwoResourcePoolError(TwoResourcePoolError),
806
807    MultiResourcePoolError(MultiResourcePoolError),
808}
809
810impl From<TransactionProcessorError> for ApplicationError {
811    fn from(value: TransactionProcessorError) -> Self {
812        Self::TransactionProcessorError(value)
813    }
814}
815
816impl From<PackageError> for ApplicationError {
817    fn from(value: PackageError) -> Self {
818        Self::PackageError(value)
819    }
820}
821
822impl From<ConsensusManagerError> for ApplicationError {
823    fn from(value: ConsensusManagerError) -> Self {
824        Self::ConsensusManagerError(value)
825    }
826}
827
828impl From<FungibleResourceManagerError> for ApplicationError {
829    fn from(value: FungibleResourceManagerError) -> Self {
830        Self::FungibleResourceManagerError(value)
831    }
832}
833
834impl From<RoleAssignmentError> for ApplicationError {
835    fn from(value: RoleAssignmentError) -> Self {
836        Self::RoleAssignmentError(value)
837    }
838}
839
840impl From<BucketError> for ApplicationError {
841    fn from(value: BucketError) -> Self {
842        Self::BucketError(value)
843    }
844}
845
846impl From<ProofError> for ApplicationError {
847    fn from(value: ProofError) -> Self {
848        Self::ProofError(value)
849    }
850}
851
852impl From<VaultError> for ApplicationError {
853    fn from(value: VaultError) -> Self {
854        Self::VaultError(value)
855    }
856}
857
858impl From<WorktopError> for ApplicationError {
859    fn from(value: WorktopError) -> Self {
860        Self::WorktopError(value)
861    }
862}
863
864impl From<AuthZoneError> for ApplicationError {
865    fn from(value: AuthZoneError) -> Self {
866        Self::AuthZoneError(value)
867    }
868}
869
870impl From<OpenSubstateError> for CallFrameError {
871    fn from(value: OpenSubstateError) -> Self {
872        Self::OpenSubstateError(value)
873    }
874}
875
876impl From<CloseSubstateError> for CallFrameError {
877    fn from(value: CloseSubstateError) -> Self {
878        Self::CloseSubstateError(value)
879    }
880}
881
882impl From<PassMessageError> for CallFrameError {
883    fn from(value: PassMessageError) -> Self {
884        Self::PassMessageError(value)
885    }
886}
887
888impl From<MovePartitionError> for CallFrameError {
889    fn from(value: MovePartitionError) -> Self {
890        Self::MovePartitionError(value)
891    }
892}
893
894impl From<ReadSubstateError> for CallFrameError {
895    fn from(value: ReadSubstateError) -> Self {
896        Self::ReadSubstateError(value)
897    }
898}
899
900impl From<WriteSubstateError> for CallFrameError {
901    fn from(value: WriteSubstateError) -> Self {
902        Self::WriteSubstateError(value)
903    }
904}
905
906impl From<CreateNodeError> for CallFrameError {
907    fn from(value: CreateNodeError) -> Self {
908        Self::CreateNodeError(value)
909    }
910}
911
912impl From<DropNodeError> for CallFrameError {
913    fn from(value: DropNodeError) -> Self {
914        Self::DropNodeError(value)
915    }
916}
917
918impl From<CreateFrameError> for CallFrameError {
919    fn from(value: CreateFrameError) -> Self {
920        Self::CreateFrameError(value)
921    }
922}
923
924impl From<CallFrameScanKeysError> for CallFrameError {
925    fn from(value: CallFrameScanKeysError) -> Self {
926        Self::ScanSubstatesError(value)
927    }
928}
929
930impl From<CallFrameScanSortedSubstatesError> for CallFrameError {
931    fn from(value: CallFrameScanSortedSubstatesError) -> Self {
932        Self::ScanSortedSubstatesError(value)
933    }
934}
935
936impl From<CallFrameDrainSubstatesError> for CallFrameError {
937    fn from(value: CallFrameDrainSubstatesError) -> Self {
938        Self::DrainSubstatesError(value)
939    }
940}
941
942impl From<CallFrameSetSubstateError> for CallFrameError {
943    fn from(value: CallFrameSetSubstateError) -> Self {
944        Self::SetSubstatesError(value)
945    }
946}
947
948impl From<CallFrameRemoveSubstateError> for CallFrameError {
949    fn from(value: CallFrameRemoveSubstateError) -> Self {
950        Self::RemoveSubstatesError(value)
951    }
952}
953
954impl<T> From<T> for RuntimeError
955where
956    T: Into<CallFrameError>,
957{
958    fn from(value: T) -> Self {
959        Self::KernelError(KernelError::CallFrameError(value.into()))
960    }
961}
962
963#[cfg(test)]
964mod tests {
965    use super::*;
966
967    #[test]
968    fn the_current_runtime_error_schema_is_last_on_historic_list() {
969        let latest = HISTORIC_RUNTIME_ERROR_SCHEMAS.last().unwrap();
970        let current = generate_single_type_schema::<RuntimeError, ScryptoCustomSchema>();
971
972        // If this test fails, see the comment above `RuntimeError` for instructions.
973        compare_single_type_schemas(
974            &SchemaComparisonSettings::require_equality(),
975            latest,
976            &current,
977        )
978        .assert_valid("latest", "current");
979    }
980
981    #[test]
982    fn the_current_runtime_error_schema_has_no_raw_node_ids() {
983        let current = generate_single_type_schema::<RuntimeError, ScryptoCustomSchema>();
984        assert_no_raw_node_ids(&current);
985    }
986
987    #[test]
988    fn the_current_rejection_reason_schema_is_last_on_historic_list() {
989        let latest = HISTORIC_REJECTION_REASON_SCHEMAS.last().unwrap();
990        let current = generate_single_type_schema::<RejectionReason, ScryptoCustomSchema>();
991
992        // If this test fails, see the comment above `RejectionReason` for instructions.
993        compare_single_type_schemas(
994            &SchemaComparisonSettings::require_equality(),
995            latest,
996            &current,
997        )
998        .assert_valid("latest", "current");
999    }
1000
1001    #[test]
1002    fn the_current_rejection_reason_schema_has_no_raw_node_ids() {
1003        let current = generate_single_type_schema::<RejectionReason, ScryptoCustomSchema>();
1004        assert_no_raw_node_ids(&current);
1005    }
1006
1007    fn assert_no_raw_node_ids(schema: &SingleTypeSchema<ScryptoCustomSchema>) {
1008        let schema = schema.schema.as_unique_version();
1009        for (type_kind, type_metadata) in schema.type_kinds.iter().zip(schema.type_metadata.iter())
1010        {
1011            if type_metadata.type_name.as_deref() == Some("NodeId") {
1012                match type_kind {
1013                    TypeKind::Custom(ScryptoCustomTypeKind::Own)
1014                    | TypeKind::Custom(ScryptoCustomTypeKind::Reference) => {}
1015                    _ => {
1016                        let mut formatted_schema = String::new();
1017                        format_debug_like_value(
1018                            &mut formatted_schema,
1019                            &generate_single_type_schema::<
1020                                SchemaV1<ScryptoCustomSchema>,
1021                                ScryptoCustomSchema,
1022                            >(),
1023                            &scrypto_decode(&scrypto_encode(schema).unwrap()).unwrap(),
1024                            PrintMode::MultiLine {
1025                                indent_size: 4,
1026                                base_indent: 4,
1027                                first_line_indent: 4,
1028                            },
1029                            ScryptoValueDisplayContext::default(),
1030                        );
1031                        // If this is too much for the console, use:
1032                        // cargo test --package radix-engine --lib -- errors::tests::the_current_rejection_reason_schema_has_no_raw_node_ids --exact --show-output > output.txt
1033                        // And then check for some of the type definitions of the type names preceeding the last mention of "NodeId"
1034                        // One of these types will directly mention `NodeId` instead of `error_models::ReferencedNodeId` or `error_models::OwnedNodeId`.
1035                        panic!("A raw NodeId was detected somewhere in the error schema. Use `error_models::ReferencedNodeId` or `error_models::OwnedNodeId` instead.\n\nSchema:\n{}", formatted_schema);
1036                    }
1037                }
1038            }
1039        }
1040    }
1041
1042    #[test]
1043    fn runtime_error_string() {
1044        let network = NetworkDefinition::mainnet();
1045        let address_encoder = AddressBech32Encoder::new(&network);
1046        let address_encoder = Some(&address_encoder);
1047
1048        // Example one - Account withdraw/lock fee/create proof error
1049        {
1050            let runtime_error = RuntimeError::ApplicationError(ApplicationError::AccountError(
1051                AccountError::VaultDoesNotExist {
1052                    resource_address: XRD,
1053                },
1054            ));
1055
1056            // Old error
1057            let debugged = format!("{:?}", runtime_error);
1058            assert_eq!(debugged, "ApplicationError(AccountError(VaultDoesNotExist { resource_address: ResourceAddress(5da66318c6318c61f5a61b4c6318c6318cf794aa8d295f14e6318c6318c6) }))");
1059
1060            // New error
1061            let rendered = runtime_error.to_string(address_encoder);
1062            assert_eq!(rendered, "ApplicationError(AccountError(VaultDoesNotExist { resource_address: ResourceAddress(\"resource_rdx1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxradxrd\") }))");
1063        }
1064
1065        // Example two - dangling bucket error
1066        {
1067            let mut id_allocator = crate::kernel::id_allocator::IdAllocator::new(hash("seed-data"));
1068
1069            // Unfortunately buckets didn't get their own entity type...
1070            let bucket_entity_type = EntityType::InternalGenericComponent;
1071            let example_bucket_1 = id_allocator.allocate_node_id(bucket_entity_type).unwrap();
1072            let example_bucket_2 = id_allocator.allocate_node_id(bucket_entity_type).unwrap();
1073            let runtime_error = RuntimeError::KernelError(KernelError::OrphanedNodes(vec![
1074                example_bucket_1.into(),
1075                example_bucket_2.into(),
1076            ]));
1077
1078            // Old error
1079            let debugged = format!("{:?}", runtime_error);
1080            assert_eq!(debugged, "KernelError(OrphanedNodes([NodeId(\"f82ee60dbc11caa1594fccdbb8031c41af8084344bcbe7a4c784491a7d4c\"), NodeId(\"f8abce267317b7bdd859951840ccd25f1ea7e83c538d507e0f82da7b9aed\")]))");
1081
1082            // New error
1083            let rendered = runtime_error.to_string(address_encoder);
1084            assert_eq!(rendered, "KernelError(OrphanedNodes([NodeId(\"internal_component_rdx1lqhwvrduz892zk20endmsqcugxhcppp5f0970fx8s3y35l2vv5mzfn\"), NodeId(\"internal_component_rdx1lz4uufnnz7mmmkzej5vypnxjtu0206pu2wx4qls0std8hxhd3v84yv\")]))");
1085        }
1086    }
1087}