Source code for aleph.types.message_status

from decimal import Decimal
from enum import Enum, IntEnum
from typing import Any, Dict, Optional, Sequence, Union


[docs] class MessageOrigin(str, Enum): ONCHAIN = "onchain" P2P = "p2p" IPFS = "ipfs"
[docs] class MessageStatus(str, Enum): PENDING = "pending" PROCESSED = "processed" REJECTED = "rejected" FORGOTTEN = "forgotten" REMOVING = "removing" REMOVED = "removed"
[docs] class MessageProcessingStatus(str, Enum): PROCESSED_NEW_MESSAGE = "processed" PROCESSED_CONFIRMATION = "confirmed" FAILED_WILL_RETRY = "retry" FAILED_REJECTED = "rejected"
[docs] def to_message_status(self) -> MessageStatus: if self == self.PROCESSED_CONFIRMATION or self == self.PROCESSED_NEW_MESSAGE: return MessageStatus.PROCESSED elif self == self.FAILED_WILL_RETRY: return MessageStatus.PENDING else: return MessageStatus.REJECTED
[docs] class ErrorCode(IntEnum): INTERNAL_ERROR = -1 INVALID_FORMAT = 0 INVALID_SIGNATURE = 1 PERMISSION_DENIED = 2 CONTENT_UNAVAILABLE = 3 FILE_UNAVAILABLE = 4 BALANCE_INSUFFICIENT = 5 CREDIT_INSUFFICIENT = 6 POST_AMEND_NO_TARGET = 100 POST_AMEND_TARGET_NOT_FOUND = 101 POST_AMEND_AMEND = 102 STORE_REF_NOT_FOUND = 200 STORE_UPDATE_UPDATE = 201 INVALID_PAYMENT_METHOD = 202 VM_REF_NOT_FOUND = 300 VM_VOLUME_NOT_FOUND = 301 VM_AMEND_NOT_ALLOWED = 302 VM_UPDATE_UPDATE = 303 VM_VOLUME_TOO_SMALL = 304 FORGET_NO_TARGET = 500 FORGET_TARGET_NOT_FOUND = 501 FORGET_FORGET = 502 FORGET_NOT_ALLOWED = 503 FORGOTTEN_DUPLICATE = 504
[docs] class RemovedMessageReason(str, Enum): BALANCE_INSUFFICIENT = "balance_insufficient" CREDIT_INSUFFICIENT = "credit_insufficient"
[docs] class MessageProcessingException(Exception): error_code: ErrorCode def __init__( self, errors: Optional[Union[str, Sequence[Any]]] = None, ): if errors is None: errors = [] elif isinstance(errors, str): errors = [errors] super().__init__(errors) def __str__(self): # TODO: reimplement for each exception subtype return self.__class__.__name__
[docs] def details(self) -> Optional[Dict[str, Any]]: """ Return error details in a JSON serializable format. Returns: Dictionary with error details or None if no errors. """ errors = self.args[0] # Ensure errors are JSON serializable if errors: # Convert non-serializable objects to strings if needed serializable_errors = [] for err in errors: try: # Test if the error is JSON serializable by attempting to convert to dict # This will fail for custom objects if hasattr(err, "__dict__"): serializable_errors.append(str(err)) else: serializable_errors.append(err) except (TypeError, ValueError): # If conversion fails, use string representation serializable_errors.append(str(err)) return {"errors": serializable_errors} return None
[docs] class InvalidMessageException(MessageProcessingException): """ The message is invalid and should be rejected. """ ...
[docs] class RetryMessageException(MessageProcessingException): """ The message should be retried. """ ...
[docs] class InternalError(RetryMessageException): """ An unexpected situation occurred. """ error_code = ErrorCode.INTERNAL_ERROR
[docs] class InvalidMessageFormat(InvalidMessageException): """ The message is invalid because it is not properly formatted: missing field(s), incorrect value types, etc. """ error_code = ErrorCode.INVALID_FORMAT
[docs] class InvalidSignature(InvalidMessageException): """ The message is invalid, in particular because its signature does not match the expected value. """ error_code = ErrorCode.INVALID_SIGNATURE
[docs] class PermissionDenied(InvalidMessageException): """ The sender does not have the permission to perform the requested operation on the specified object. """ error_code = ErrorCode.PERMISSION_DENIED
[docs] class FileNotFoundException(RetryMessageException): """ A file required to process the message could not be found, locally and/or on the network. """ def __init__(self, file_hash: str, details: Optional[str] = None): message = f"File not found: {file_hash}" if details: message = f"{message} ({details})" super().__init__(message) self.file_hash = file_hash
[docs] class MessageContentUnavailable(FileNotFoundException): """ The message content is not available at the moment (storage/IPFS item types). """ error_code = ErrorCode.CONTENT_UNAVAILABLE
[docs] class FileUnavailable(FileNotFoundException): """ A file pointed to by the message is not available at the moment. """ error_code = ErrorCode.FILE_UNAVAILABLE
[docs] class NoAmendTarget(InvalidMessageException): """ A POST with type = amend does not specify a value in the ref field. """ error_code = ErrorCode.POST_AMEND_NO_TARGET
[docs] class AmendTargetNotFound(RetryMessageException): """ The original post for an amend could not be found. """ error_code = ErrorCode.POST_AMEND_TARGET_NOT_FOUND
[docs] class CannotAmendAmend(InvalidMessageException): """ The original post targeted by an amend is an amend itself, which is forbidden. """ error_code = ErrorCode.POST_AMEND_AMEND
[docs] class NoForgetTarget(InvalidMessageException): """ The FORGET message specifies nothing to forget. """ error_code = ErrorCode.FORGET_NO_TARGET
[docs] class StoreRefNotFound(RetryMessageException): """ The original store message hash specified in the `ref` field could not be found. """ error_code = ErrorCode.STORE_REF_NOT_FOUND
[docs] class StoreCannotUpdateStoreWithRef(InvalidMessageException): """ The store message targeted by the `ref` field has a value in the `ref` field itself. Update trees are not supported. """ error_code = ErrorCode.STORE_UPDATE_UPDATE
[docs] class InvalidPaymentMethod(InvalidMessageException): """ Messages with non-credit payment types are no longer allowed after the cutoff. Only credit payment is supported for STORE, INSTANCE, and PROGRAM messages. """ error_code = ErrorCode.INVALID_PAYMENT_METHOD
[docs] class ForgetNotAllowed(InvalidMessageException): """ The store message targeted by the `ref` field has a value in the `ref` field of a dependent volume. """ def __init__(self, file_hash: str, vm_hash: str): super().__init__(f"File {file_hash} used on vm {vm_hash}") error_code = ErrorCode.FORGET_NOT_ALLOWED
[docs] class VmRefNotFound(RetryMessageException): """ The original program specified in the `ref` field could not be found. """ error_code = ErrorCode.VM_REF_NOT_FOUND
[docs] class VmVolumeNotFound(RetryMessageException): """ One or more volume files could not be found. """ error_code = ErrorCode.VM_VOLUME_NOT_FOUND
[docs] class VmUpdateNotAllowed(InvalidMessageException): """ The message attempts to amend an immutable program, i.e. for which allow_amend is set to False. """ error_code = ErrorCode.VM_AMEND_NOT_ALLOWED
[docs] class VmCannotUpdateUpdate(InvalidMessageException): """ The program hash in the `replaces` field has a value for the `replaces` field itself. Update trees are not supported. """ error_code = ErrorCode.VM_UPDATE_UPDATE
[docs] class VmVolumeTooSmall(InvalidMessageException): """ A volume with a parent volume has a size inferior to the size of the parent. Ex: attempting to use a 4GB Ubuntu rootfs to a 2GB volume. """ error_code = ErrorCode.VM_VOLUME_TOO_SMALL def __init__( self, volume_name: str, volume_size: int, parent_ref: str, parent_file: str, parent_size: int, ): self.volume_name = volume_name self.volume_size = volume_size self.parent_ref = parent_ref self.parent_file = parent_file self.parent_size = parent_size
[docs] def details(self) -> Optional[Dict[str, Any]]: """ Return error details in a JSON serializable format. Returns: Dictionary with error details. """ # Ensure all values are JSON serializable return { "errors": [ { "volume_name": str(self.volume_name), "parent_ref": str(self.parent_ref), "parent_file": str(self.parent_file), "parent_size": int(self.parent_size), "volume_size": int(self.volume_size), } ] }
[docs] class InsufficientCreditException(InvalidMessageException): """ The user does not have enough Aleph credits to process the message. """ error_code = ErrorCode.CREDIT_INSUFFICIENT def __init__( self, credit_balance: int, required_credits: Decimal, min_runtime_days: int = 1, ): self.credit_balance = credit_balance self.required_credits = required_credits self.min_runtime_days = min_runtime_days
[docs] def details(self) -> Optional[Dict[str, Any]]: """ Return error details in a JSON serializable format. Returns: Dictionary with error details. """ return { "errors": [ { "required_credits": str(self.required_credits), "account_credits": str(self.credit_balance), "min_runtime_days": self.min_runtime_days, } ] }
[docs] class ForgetTargetNotFound(RetryMessageException): """ A target specified in the FORGET message could not be found. """ error_code = ErrorCode.FORGET_TARGET_NOT_FOUND def __init__( self, target_hash: Optional[str] = None, aggregate_key: Optional[str] = None ): self.target_hash = target_hash self.aggregate_key = aggregate_key
[docs] def details(self) -> Optional[Dict[str, Any]]: """ Return error details in a JSON serializable format. Returns: Dictionary with error details. """ errors = [] if self.target_hash is not None: errors.append({"message": str(self.target_hash)}) if self.aggregate_key is not None: errors.append({"aggregate": str(self.aggregate_key)}) return {"errors": errors}
[docs] class CannotForgetForgetMessage(InvalidMessageException): """ The FORGET message targets another FORGET message, which is forbidden. """ error_code = ErrorCode.FORGET_FORGET def __init__(self, target_hash: str): self.target_hash = target_hash
[docs] def details(self) -> Optional[Dict[str, Any]]: """ Return error details in a JSON serializable format. Returns: Dictionary with error details. """ return {"errors": [{"message": str(self.target_hash)}]}
[docs] class InsufficientBalanceException(InvalidMessageException): """ The user does not have enough Aleph tokens to process the message. """ error_code = ErrorCode.BALANCE_INSUFFICIENT def __init__( self, balance: Decimal, required_balance: Decimal, ): self.balance = balance self.required_balance = required_balance
[docs] def details(self) -> Optional[Dict[str, Any]]: """ Return error details in a JSON serializable format. Returns: Dictionary with error details. """ # Note: cast to string to keep the precision and ensure it's JSON serializable return { "errors": [ { "required_balance": str(self.required_balance), "account_balance": str(self.balance), } ] }