mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-16 11:28:14 +00:00
src/trezor/wire: add exceptions for all defined FailureTypes
Makes the error API much more ergonomic.
This commit is contained in:
parent
7df4b251ea
commit
db696b23fd
@ -1,5 +1,5 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.messages import ButtonRequestType, FailureType, wire_types
|
||||
from trezor.messages import ButtonRequestType, wire_types
|
||||
from trezor.messages.ButtonRequest import ButtonRequest
|
||||
from trezor.ui.confirm import CONFIRMED, ConfirmDialog, HoldToConfirmDialog
|
||||
|
||||
@ -29,10 +29,10 @@ async def hold_to_confirm(ctx, content, code=None, *args, **kwargs):
|
||||
async def require_confirm(*args, **kwargs):
|
||||
confirmed = await confirm(*args, **kwargs)
|
||||
if not confirmed:
|
||||
raise wire.FailureError(FailureType.ActionCancelled, 'Cancelled')
|
||||
raise wire.ActionCancelled('Cancelled')
|
||||
|
||||
|
||||
async def require_hold_to_confirm(*args, **kwargs):
|
||||
confirmed = await hold_to_confirm(*args, **kwargs)
|
||||
if not confirmed:
|
||||
raise wire.FailureError(FailureType.ActionCancelled, 'Cancelled')
|
||||
raise wire.ActionCancelled('Cancelled')
|
||||
|
@ -1,7 +1,6 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.messages import ButtonRequestType, wire_types
|
||||
from trezor.messages.ButtonRequest import ButtonRequest
|
||||
from trezor.messages.FailureType import ActionCancelled, ProcessError
|
||||
from trezor.messages import PassphraseSourceType
|
||||
from trezor.messages.PassphraseRequest import PassphraseRequest
|
||||
from trezor.messages.PassphraseStateRequest import PassphraseStateRequest
|
||||
@ -24,7 +23,7 @@ async def request_passphrase_entry(ctx):
|
||||
wire_types.ButtonAck,
|
||||
wire_types.Cancel)
|
||||
if ack.MESSAGE_WIRE_TYPE == wire_types.Cancel:
|
||||
raise wire.FailureError(ActionCancelled, 'Passphrase cancelled')
|
||||
raise wire.ActionCancelled('Passphrase cancelled')
|
||||
|
||||
selector = EntrySelector(text)
|
||||
return await ctx.wait(selector)
|
||||
@ -41,18 +40,18 @@ async def request_passphrase_ack(ctx, on_device):
|
||||
req = PassphraseRequest(on_device=on_device)
|
||||
ack = await ctx.call(req, wire_types.PassphraseAck, wire_types.Cancel)
|
||||
if ack.MESSAGE_WIRE_TYPE == wire_types.Cancel:
|
||||
raise wire.FailureError(ActionCancelled, 'Passphrase cancelled')
|
||||
raise wire.ActionCancelled('Passphrase cancelled')
|
||||
|
||||
if on_device:
|
||||
if ack.passphrase is not None:
|
||||
raise wire.FailureError(ProcessError, 'Passphrase provided when it should not be')
|
||||
raise wire.ProcessError('Passphrase provided when it should not be')
|
||||
keyboard = PassphraseKeyboard('Enter passphrase')
|
||||
passphrase = await ctx.wait(keyboard)
|
||||
if passphrase == CANCELLED:
|
||||
raise wire.FailureError(ActionCancelled, 'Passphrase cancelled')
|
||||
raise wire.ActionCancelled('Passphrase cancelled')
|
||||
else:
|
||||
if ack.passphrase is None:
|
||||
raise wire.FailureError(ProcessError, 'Passphrase not provided')
|
||||
raise wire.ProcessError('Passphrase not provided')
|
||||
passphrase = ack.passphrase
|
||||
|
||||
req = PassphraseStateRequest(state=get_state(state=ack.state, passphrase=passphrase))
|
||||
|
@ -1,6 +1,5 @@
|
||||
from trezor import wire
|
||||
from trezor.crypto import bip32, bip39
|
||||
from trezor.messages.FailureType import ProcessError
|
||||
from apps.common import cache, storage
|
||||
from apps.common.request_passphrase import protect_by_passphrase
|
||||
|
||||
@ -24,7 +23,7 @@ async def _get_seed(ctx: wire.Context) -> bytes:
|
||||
|
||||
async def _compute_seed(ctx: wire.Context) -> (bytes, str):
|
||||
if not storage.is_initialized():
|
||||
raise wire.FailureError(ProcessError, 'Device is not initialized')
|
||||
raise wire.ProcessError('Device is not initialized')
|
||||
|
||||
passphrase = await protect_by_passphrase(ctx)
|
||||
return bip39.seed(storage.get_mnemonic(), passphrase), passphrase
|
||||
|
@ -1,5 +1,5 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.messages import ButtonRequestType, FailureType, PassphraseSourceType
|
||||
from trezor.messages import ButtonRequestType, PassphraseSourceType
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.ui.text import Text
|
||||
from apps.common import storage
|
||||
@ -8,11 +8,11 @@ from apps.common.confirm import require_confirm
|
||||
|
||||
async def apply_settings(ctx, msg):
|
||||
if msg.homescreen is None and msg.label is None and msg.use_passphrase is None and msg.passphrase_source is None:
|
||||
raise wire.FailureError(FailureType.ProcessError, 'No setting provided')
|
||||
raise wire.ProcessError('No setting provided')
|
||||
|
||||
if msg.homescreen is not None:
|
||||
if len(msg.homescreen) > storage.HOMESCREEN_MAXSIZE:
|
||||
raise wire.FailureError(FailureType.DataError, 'Homescreen is too complex')
|
||||
raise wire.DataError('Homescreen is too complex')
|
||||
await require_confirm(ctx, Text(
|
||||
'Change homescreen', ui.ICON_CONFIG,
|
||||
'Do you really want to', 'change homescreen?'),
|
||||
|
@ -1,16 +1,19 @@
|
||||
from trezor import wire
|
||||
from trezor.messages.FailureType import ProcessError
|
||||
from trezor.messages.Success import Success
|
||||
from apps.common import storage
|
||||
from apps.management.reset_device import (check_mnemonic, show_mnemonic,
|
||||
show_warning, show_wrong_entry)
|
||||
from apps.management.reset_device import (
|
||||
check_mnemonic,
|
||||
show_mnemonic,
|
||||
show_warning,
|
||||
show_wrong_entry,
|
||||
)
|
||||
|
||||
|
||||
async def backup_device(ctx, msg):
|
||||
if not storage.is_initialized():
|
||||
raise wire.FailureError(ProcessError, 'Device is not initialized')
|
||||
raise wire.ProcessError('Device is not initialized')
|
||||
if not storage.needs_backup():
|
||||
raise wire.FailureError(ProcessError, 'Seed already backed up')
|
||||
raise wire.ProcessError('Seed already backed up')
|
||||
|
||||
mnemonic = storage.get_mnemonic()
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
from trezor import config, loop, ui, wire
|
||||
from trezor.messages import FailureType, wire_types
|
||||
from trezor.messages import wire_types
|
||||
from trezor.messages.ButtonRequest import ButtonRequest
|
||||
from trezor.messages.ButtonRequestType import Other
|
||||
from trezor.messages.Failure import Failure
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.pin import pin_to_int, show_pin_timeout
|
||||
from trezor.ui.text import Text
|
||||
@ -19,7 +18,7 @@ async def change_pin(ctx, msg):
|
||||
if config.has_pin():
|
||||
curpin = await request_pin_ack(ctx)
|
||||
if not config.check_pin(pin_to_int(curpin), show_pin_timeout):
|
||||
return Failure(code=FailureType.PinInvalid, message='PIN invalid')
|
||||
raise wire.PinInvalid('PIN invalid')
|
||||
else:
|
||||
curpin = ''
|
||||
|
||||
@ -30,13 +29,13 @@ async def change_pin(ctx, msg):
|
||||
newpin = ''
|
||||
|
||||
# write into storage
|
||||
if config.change_pin(pin_to_int(curpin), pin_to_int(newpin), show_pin_timeout):
|
||||
if not config.change_pin(pin_to_int(curpin), pin_to_int(newpin), show_pin_timeout):
|
||||
raise wire.PinInvalid('PIN invalid')
|
||||
|
||||
if newpin:
|
||||
return Success(message='PIN changed')
|
||||
else:
|
||||
return Success(message='PIN removed')
|
||||
else:
|
||||
return Failure(code=FailureType.PinInvalid, message='PIN invalid')
|
||||
|
||||
|
||||
def require_confirm_change_pin(ctx, msg):
|
||||
@ -75,7 +74,7 @@ async def request_pin_ack(ctx, *args, **kwargs):
|
||||
await ctx.call(ButtonRequest(code=Other), wire_types.ButtonAck)
|
||||
return await ctx.wait(request_pin(*args, **kwargs))
|
||||
except PinCancelled:
|
||||
raise wire.FailureError(FailureType.ActionCancelled, 'Cancelled')
|
||||
raise wire.ActionCancelled('Cancelled')
|
||||
|
||||
|
||||
@ui.layout
|
||||
|
@ -1,6 +1,5 @@
|
||||
from trezor import config, ui, wire
|
||||
from trezor.crypto import bip39
|
||||
from trezor.messages.FailureType import ProcessError, UnexpectedMessage
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.pin import pin_to_int
|
||||
from trezor.ui.text import Text
|
||||
@ -11,13 +10,13 @@ from apps.common.confirm import require_confirm
|
||||
async def load_device(ctx, msg):
|
||||
|
||||
if storage.is_initialized():
|
||||
raise wire.FailureError(UnexpectedMessage, 'Already initialized')
|
||||
raise wire.UnexpectedMessage('Already initialized')
|
||||
|
||||
if msg.node is not None:
|
||||
raise wire.FailureError(ProcessError, 'LoadDevice.node is not supported')
|
||||
raise wire.ProcessError('LoadDevice.node is not supported')
|
||||
|
||||
if not msg.skip_checksum and not bip39.check(msg.mnemonic):
|
||||
raise wire.FailureError(ProcessError, 'Mnemonic is not valid')
|
||||
raise wire.ProcessError('Mnemonic is not valid')
|
||||
|
||||
await require_confirm(ctx, Text(
|
||||
'Loading seed', ui.ICON_DEFAULT,
|
||||
|
@ -25,7 +25,7 @@ async def recovery_device(ctx, msg):
|
||||
5. Save into storage.
|
||||
'''
|
||||
if not msg.dry_run and storage.is_initialized():
|
||||
raise wire.FailureError(UnexpectedMessage, 'Already initialized')
|
||||
raise wire.UnexpectedMessage('Already initialized')
|
||||
|
||||
# ask for the number of words
|
||||
wordcount = await request_wordcount(ctx)
|
||||
@ -36,7 +36,7 @@ async def recovery_device(ctx, msg):
|
||||
# check mnemonic validity
|
||||
if msg.enforce_wordlist or msg.dry_run:
|
||||
if not bip39.check(mnemonic):
|
||||
raise wire.FailureError(ProcessError, 'Mnemonic is not valid')
|
||||
raise wire.ProcessError('Mnemonic is not valid')
|
||||
|
||||
# ask for pin repeatedly
|
||||
if msg.pin_protection:
|
||||
@ -55,7 +55,7 @@ async def recovery_device(ctx, msg):
|
||||
if storage.get_mnemonic() == mnemonic:
|
||||
return Success(message='The seed is valid and matches the one in the device')
|
||||
else:
|
||||
raise wire.FailureError(ProcessError, 'The seed is valid but does not match the one in the device')
|
||||
raise wire.ProcessError('The seed is valid but does not match the one in the device')
|
||||
|
||||
|
||||
@ui.layout
|
||||
|
@ -1,7 +1,7 @@
|
||||
from micropython import const
|
||||
from trezor import config, ui, wire
|
||||
from trezor.crypto import bip39, hashlib, random
|
||||
from trezor.messages import ButtonRequestType, FailureType, wire_types
|
||||
from trezor.messages import ButtonRequestType, wire_types
|
||||
from trezor.messages.ButtonRequest import ButtonRequest
|
||||
from trezor.messages.EntropyRequest import EntropyRequest
|
||||
from trezor.messages.Success import Success
|
||||
@ -24,13 +24,9 @@ if __debug__:
|
||||
async def reset_device(ctx, msg):
|
||||
# validate parameters and device state
|
||||
if msg.strength not in (128, 192, 256):
|
||||
raise wire.FailureError(
|
||||
FailureType.ProcessError,
|
||||
'Invalid strength (has to be 128, 192 or 256 bits)')
|
||||
raise wire.ProcessError('Invalid strength (has to be 128, 192 or 256 bits)')
|
||||
if storage.is_initialized():
|
||||
raise wire.FailureError(
|
||||
FailureType.UnexpectedMessage,
|
||||
'Already initialized')
|
||||
raise wire.UnexpectedMessage('Already initialized')
|
||||
|
||||
# request new PIN
|
||||
if msg.pin_protection:
|
||||
@ -62,8 +58,7 @@ async def reset_device(ctx, msg):
|
||||
|
||||
# write PIN into storage
|
||||
if not config.change_pin(pin_to_int(''), pin_to_int(newpin), None):
|
||||
raise wire.FailureError(
|
||||
FailureType.ProcessError, 'Could not change PIN')
|
||||
raise wire.ProcessError('Could not change PIN')
|
||||
|
||||
# write settings and mnemonic into storage
|
||||
storage.load_settings(
|
||||
|
@ -1,5 +1,5 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.messages import ButtonRequestType, FailureType
|
||||
from trezor.messages import ButtonRequestType
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.ui.text import Text
|
||||
from apps.common import storage
|
||||
@ -8,7 +8,7 @@ from apps.common.confirm import require_confirm
|
||||
|
||||
async def set_u2f_counter(ctx, msg):
|
||||
if msg.u2f_counter is None:
|
||||
raise wire.FailureError(FailureType.ProcessError, 'No value provided provided')
|
||||
raise wire.ProcessError('No value provided')
|
||||
|
||||
await require_confirm(ctx, Text(
|
||||
'Set U2F counter', ui.ICON_CONFIG,
|
||||
|
@ -3,7 +3,6 @@ from trezor.crypto import hmac
|
||||
from trezor.crypto.aes import AES_CBC_Decrypt, AES_CBC_Encrypt
|
||||
from trezor.crypto.hashlib import sha512
|
||||
from trezor.messages.CipheredKeyValue import CipheredKeyValue
|
||||
from trezor.messages.FailureType import DataError
|
||||
from trezor.ui.text import Text, TEXT_MARGIN_LEFT
|
||||
from trezor.utils import split_words
|
||||
from apps.common import seed
|
||||
@ -12,8 +11,7 @@ from apps.common.confirm import require_confirm
|
||||
|
||||
async def cipher_key_value(ctx, msg):
|
||||
if len(msg.value) % 16 > 0:
|
||||
raise wire.FailureError(
|
||||
DataError, 'Value length must be a multiple of 16')
|
||||
raise wire.DataError('Value length must be a multiple of 16')
|
||||
|
||||
encrypt = msg.encrypt
|
||||
decrypt = not msg.encrypt
|
||||
|
@ -1,5 +1,4 @@
|
||||
from trezor import ui
|
||||
from trezor.wire import FailureError
|
||||
from trezor import ui, wire
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS
|
||||
from trezor.messages.FailureType import ProcessError
|
||||
@ -34,7 +33,7 @@ async def sign_message(ctx, msg):
|
||||
elif script_type == SPENDWITNESS:
|
||||
signature = bytes([signature[0] + 8]) + signature[1:]
|
||||
else:
|
||||
raise FailureError(ProcessError, 'Unsupported script type')
|
||||
raise wire.ProcessError('Unsupported script type')
|
||||
|
||||
return MessageSignature(address=address, signature=signature)
|
||||
|
||||
|
@ -17,15 +17,15 @@ async def sign_tx(ctx, msg):
|
||||
try:
|
||||
req = signer.send(res)
|
||||
except signing.SigningError as e:
|
||||
raise wire.FailureError(*e.args)
|
||||
raise wire.Error(*e.args)
|
||||
except signing.MultisigError as e:
|
||||
raise wire.FailureError(*e.args)
|
||||
raise wire.Error(*e.args)
|
||||
except signing.AddressError as e:
|
||||
raise wire.FailureError(*e.args)
|
||||
raise wire.Error(*e.args)
|
||||
except signing.ScriptsError as e:
|
||||
raise wire.FailureError(*e.args)
|
||||
raise wire.Error(*e.args)
|
||||
except signing.Bip143Error as e:
|
||||
raise wire.FailureError(*e.args)
|
||||
raise wire.Error(*e.args)
|
||||
if req.__qualname__ == 'TxRequest':
|
||||
if req.request_type == TXFINISHED:
|
||||
break
|
||||
|
@ -1,7 +1,6 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS
|
||||
from trezor.messages.FailureType import ProcessError
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.ui.text import Text
|
||||
from apps.common import coins
|
||||
@ -31,12 +30,12 @@ async def verify_message(ctx, msg):
|
||||
script_type = SPENDWITNESS # native segwit
|
||||
signature = bytes([signature[0] - 8]) + signature[1:]
|
||||
else:
|
||||
raise wire.FailureError(ProcessError, 'Invalid signature')
|
||||
raise wire.ProcessError('Invalid signature')
|
||||
|
||||
pubkey = secp256k1.verify_recover(signature, digest)
|
||||
|
||||
if not pubkey:
|
||||
raise wire.FailureError(ProcessError, 'Invalid signature')
|
||||
raise wire.ProcessError('Invalid signature')
|
||||
|
||||
if script_type == SPENDADDRESS:
|
||||
addr = address_pkh(pubkey, coin.address_type)
|
||||
@ -45,10 +44,10 @@ async def verify_message(ctx, msg):
|
||||
elif script_type == SPENDWITNESS:
|
||||
addr = address_p2wpkh(pubkey, coin.bech32_prefix)
|
||||
else:
|
||||
raise wire.FailureError(ProcessError, 'Invalid signature')
|
||||
raise wire.ProcessError('Invalid signature')
|
||||
|
||||
if addr != address:
|
||||
raise wire.FailureError(ProcessError, 'Invalid signature')
|
||||
raise wire.ProcessError('Invalid signature')
|
||||
|
||||
await require_confirm_verify_message(ctx, address, message)
|
||||
|
||||
|
@ -1,12 +1,7 @@
|
||||
import protobuf
|
||||
|
||||
from trezor import log
|
||||
from trezor import loop
|
||||
from trezor import messages
|
||||
from trezor import utils
|
||||
from trezor import workflow
|
||||
|
||||
from . import codec_v1
|
||||
from trezor import log, loop, messages, utils, workflow
|
||||
from trezor.wire import codec_v1
|
||||
from trezor.wire.errors import *
|
||||
|
||||
workflow_handlers = {}
|
||||
|
||||
@ -101,13 +96,6 @@ class UnexpectedMessageError(Exception):
|
||||
self.reader = reader
|
||||
|
||||
|
||||
class FailureError(Exception):
|
||||
def __init__(self, code, message):
|
||||
super().__init__()
|
||||
self.code = code
|
||||
self.message = message
|
||||
|
||||
|
||||
async def session_handler(iface, sid):
|
||||
reader = None
|
||||
ctx = Context(iface, sid)
|
||||
@ -135,8 +123,8 @@ async def session_handler(iface, sid):
|
||||
# retry with opened reader from the exception
|
||||
reader = exc.reader
|
||||
continue
|
||||
except FailureError as exc:
|
||||
# we log FailureError as warning, not as exception
|
||||
except Error as exc:
|
||||
# we log wire.Error as warning, not as exception
|
||||
log.warning(__name__, 'failure: %s', exc.message)
|
||||
except Exception as exc:
|
||||
# sessions are never closed by raised exceptions
|
||||
@ -148,7 +136,6 @@ async def session_handler(iface, sid):
|
||||
|
||||
async def protobuf_workflow(ctx, reader, handler, *args):
|
||||
from trezor.messages.Failure import Failure
|
||||
from trezor.messages.FailureType import FirmwareError
|
||||
|
||||
req = await protobuf.load_message(reader, messages.get_type(reader.type))
|
||||
try:
|
||||
@ -156,13 +143,13 @@ async def protobuf_workflow(ctx, reader, handler, *args):
|
||||
except UnexpectedMessageError:
|
||||
# session handler takes care of this one
|
||||
raise
|
||||
except FailureError as exc:
|
||||
except Error as exc:
|
||||
# respond with specific code and message
|
||||
await ctx.write(Failure(code=exc.code, message=exc.message))
|
||||
raise
|
||||
except Exception as exc:
|
||||
# respond with a generic code and message
|
||||
await ctx.write(Failure(code=FirmwareError, message='Firmware error'))
|
||||
await ctx.write(Failure(code=FailureType.FirmwareError, message='Firmware error'))
|
||||
raise
|
||||
if res:
|
||||
# respond with a specific response
|
||||
@ -171,7 +158,6 @@ async def protobuf_workflow(ctx, reader, handler, *args):
|
||||
|
||||
async def unexpected_msg(ctx, reader):
|
||||
from trezor.messages.Failure import Failure
|
||||
from trezor.messages.FailureType import UnexpectedMessage
|
||||
|
||||
# receive the message and throw it away
|
||||
while reader.size > 0:
|
||||
@ -179,5 +165,4 @@ async def unexpected_msg(ctx, reader):
|
||||
await reader.areadinto(buf)
|
||||
|
||||
# respond with an unknown message error
|
||||
await ctx.write(
|
||||
Failure(code=UnexpectedMessage, message='Unexpected message'))
|
||||
await ctx.write(Failure(code=FailureType.UnexpectedMessage, message='Unexpected message'))
|
||||
|
73
src/trezor/wire/errors.py
Normal file
73
src/trezor/wire/errors.py
Normal file
@ -0,0 +1,73 @@
|
||||
from trezor.messages import FailureType
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
def __init__(self, code, message):
|
||||
super().__init__()
|
||||
self.code = code
|
||||
self.message = message
|
||||
|
||||
|
||||
class UnexpectedMessage(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.UnexpectedMessage, message)
|
||||
|
||||
|
||||
class ButtonExpected(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.ButtonExpected, message)
|
||||
|
||||
|
||||
class DataError(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.DataError, message)
|
||||
|
||||
|
||||
class ActionCancelled(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.ActionCancelled, message)
|
||||
|
||||
|
||||
class PinExpected(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.PinExpected, message)
|
||||
|
||||
|
||||
class PinCancelled(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.PinCancelled, message)
|
||||
|
||||
|
||||
class PinInvalid(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.PinInvalid, message)
|
||||
|
||||
|
||||
class InvalidSignature(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.InvalidSignature, message)
|
||||
|
||||
|
||||
class ProcessError(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.ProcessError, message)
|
||||
|
||||
|
||||
class NotEnoughFunds(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.NotEnoughFunds, message)
|
||||
|
||||
|
||||
class NotInitialized(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.NotInitialized, message)
|
||||
|
||||
|
||||
class PinMismatch(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.PinMismatch, message)
|
||||
|
||||
|
||||
class FirmwareError(Error):
|
||||
def __init__(self, message):
|
||||
super().__init__(FailureType.FirmwareError, message)
|
Loading…
Reference in New Issue
Block a user