"""Exceptions raised by the library.
Everything derives from :class:`RobonomicsError`, so a caller can catch the
library as a whole. Input errors also derive from :class:`ValueError`: they
describe bad data, and retrying them is pointless.
Messages never contain secrets: no mnemonic, seed or private key is ever
formatted into an exception, including the input that failed to parse.
"""
[docs]
class RobonomicsError(Exception):
"""Base class for every error raised by robonomics-interface."""
[docs]
class InvalidMnemonic(RobonomicsError, ValueError):
"""The text is not a valid BIP39 English mnemonic."""
[docs]
class InvalidAddress(RobonomicsError, ValueError):
"""The text is not a valid SS58 address, or it has an unexpected format."""
[docs]
class InvalidKey(RobonomicsError, ValueError):
"""A seed, public key or derivation path cannot be used."""
[docs]
class NoSecretKey(RobonomicsError):
"""The keypair holds a public key only, so it cannot sign or decrypt."""
[docs]
class DecryptionError(RobonomicsError):
"""A box does not open: wrong keys, or the ciphertext was damaged."""
[docs]
class NoSuchPallet(MetadataError, LookupError):
"""The runtime has no pallet with this name."""
[docs]
class NoSuchStorage(MetadataError, LookupError):
"""The pallet has no storage item with this name."""
[docs]
class NoSuchConstant(MetadataError, LookupError):
"""The pallet has no constant with this name."""
[docs]
class NoSuchCall(MetadataError, LookupError):
"""The pallet has no call (extrinsic function) with this name."""
[docs]
class UnsupportedExtension(MetadataError):
"""The runtime requires a signed extension this library cannot fill in."""
[docs]
class EncodeError(RobonomicsError, ValueError):
"""A value does not fit the type the runtime expects, e.g. a bad storage key."""
[docs]
class DecodeError(RobonomicsError, ValueError):
"""Bytes from the chain do not decode as the type the metadata describes."""
[docs]
class TransportError(RobonomicsError):
"""The node could not be reached or stopped answering. Retrying may help.
Every subclass is a network-level failure: the request may not have reached
the node at all, so nothing is known about its effect.
"""
retryable = True
[docs]
class ConnectionFailed(TransportError):
"""A connection to an endpoint could not be opened, or the node is unusable."""
def __init__(self, endpoint: str, reason: str) -> None:
super().__init__(f"{endpoint}: {reason}")
self.endpoint = endpoint
self.reason = reason
[docs]
class ConnectionLost(TransportError):
"""The connection closed while a request or subscription was in flight."""
[docs]
class RequestTimeout(TransportError, TimeoutError):
"""The node did not answer in time."""
[docs]
class AllEndpointsFailed(TransportError):
"""No endpoint could be used; ``failures`` says why for each of them."""
def __init__(self, failures: list[ConnectionFailed]) -> None:
reasons = "; ".join(str(failure) for failure in failures) or "no endpoints configured"
super().__init__(f"no usable Robonomics node: {reasons}")
self.failures = failures
[docs]
class RpcError(RobonomicsError):
"""The node answered with a JSON-RPC error. Repeating the same request will not help."""
def __init__(self, method: str, code: int | None, message: str, data: object = None) -> None:
detail = f": {data}" if data not in (None, "") else ""
super().__init__(f"{method} failed ({code}): {message}{detail}")
self.method = method
self.code = code
self.message = message
self.data = data
[docs]
class TransactionError(RobonomicsError):
"""An extrinsic was refused, failed, or its outcome is unknown.
Not retryable as such: resubmitting blindly may execute it twice.
"""
[docs]
class InvalidTransaction(TransactionError):
"""The runtime refuses the extrinsic before it reaches a block.
``kind`` is the ``InvalidTransaction`` (or ``UnknownTransaction``) variant,
e.g. ``"Payment"``; ``explanation`` says what it usually means here.
"""
def __init__(self, kind: str, explanation: str) -> None:
super().__init__(f"{kind}: {explanation}")
self.kind = kind
self.explanation = explanation
[docs]
class ExtrinsicFailed(TransactionError):
"""The extrinsic is in a block, and the runtime reports that the call failed.
``pallet`` and ``error`` name the module error (``RWS``,
``FreeWeightIsNotEnough``) or are ``None`` for other dispatch errors;
``result`` is the :class:`~robonomicsinterface.extrinsic.ExtrinsicResult`.
"""
def __init__(
self,
message: str,
*,
pallet: str | None,
error: str,
docs: str,
result: object,
) -> None:
super().__init__(message)
self.pallet = pallet
self.error = error
self.docs = docs
self.result = result
[docs]
class ExtrinsicDropped(TransactionError):
"""The node dropped the extrinsic before inclusion (dropped, invalid or usurped)."""
def __init__(self, extrinsic_hash: str, status: str) -> None:
super().__init__(f"extrinsic {extrinsic_hash} was {status} by the node")
self.extrinsic_hash = extrinsic_hash
self.status = status
[docs]
class ExtrinsicOutcomeUnknown(TransactionError):
"""The extrinsic was submitted, but whether it was included is not known.
The connection dropped or the wait timed out. It may still land: look it up
by ``extrinsic_hash`` before sending it again.
"""
def __init__(self, extrinsic_hash: str, reason: str) -> None:
super().__init__(f"outcome of extrinsic {extrinsic_hash} is unknown: {reason}")
self.extrinsic_hash = extrinsic_hash
self.reason = reason
[docs]
class EnvelopeError(RobonomicsError):
"""Base class for multi-recipient envelope errors."""
[docs]
class RecipientError(EnvelopeError, ValueError):
"""A recipient address cannot be used to wrap the key."""
[docs]
class PackageError(EnvelopeError, ValueError):
"""The package is malformed, or not addressed to this recipient."""
[docs]
class PayloadError(EnvelopeError):
"""The package is well formed but could not be decrypted."""