Skip to main content

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