You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/cardano/helpers/account_path_check.py

106 lines
3.7 KiB

from typing import TYPE_CHECKING
from ...common.paths import HARDENED
from ..seed import is_byron_path, is_minting_path, is_multisig_path, is_shelley_path
from . import (
INVALID_CERTIFICATE,
INVALID_OUTPUT,
INVALID_WITHDRAWAL,
INVALID_WITNESS_REQUEST,
)
from .paths import ACCOUNT_PATH_INDEX, ACCOUNT_PATH_LENGTH
from .utils import to_account_path
if TYPE_CHECKING:
from trezor import wire
from trezor.messages import (
CardanoPoolOwner,
CardanoTxCertificate,
CardanoTxOutput,
CardanoTxWitnessRequest,
CardanoTxWithdrawal,
)
class AccountPathChecker:
"""
Used to verify that all paths in a transaction which are not being shown to the user belong
to the same account. Paths are matched against the path which is added first. If there's a mismatch,
an error is raised.
"""
UNDEFINED = object()
def __init__(self) -> None:
self.account_path: object | list[int] = self.UNDEFINED
def _add(self, path: list[int], error: wire.ProcessError) -> None:
# multi-sig and minting paths are always shown and thus don't need to be checked
if is_multisig_path(path) or is_minting_path(path):
return
account_path = to_account_path(path)
if self.account_path is self.UNDEFINED:
self.account_path = account_path
elif (
self.account_path != account_path
and not self._is_byron_and_shelley_equivalent(account_path)
):
raise error
def _is_byron_and_shelley_equivalent(self, account_path: list[int]) -> bool:
"""
For historical purposes Byron path (44'/1815'/0') is considered equivalent to the Shelley
path with the same account (1852'/1815'/0'). This combination of accounts is allowed
in order to make Byron to Shelley migrations possible with the Shelley path staying hidden
from the user. This way the user can be sure that the funds are being moved between the user's
accounts without being bothered by more screens.
"""
assert isinstance(self.account_path, list)
is_control_path_byron_or_shelley = is_byron_path(
self.account_path
) or is_shelley_path(self.account_path)
is_new_path_byron_or_shelley = is_byron_path(account_path) or is_shelley_path(
account_path
)
return (
is_control_path_byron_or_shelley
and is_new_path_byron_or_shelley
and len(self.account_path) == ACCOUNT_PATH_LENGTH
and len(account_path) == ACCOUNT_PATH_LENGTH
and self.account_path[ACCOUNT_PATH_INDEX] == 0 | HARDENED
and account_path[ACCOUNT_PATH_INDEX] == 0 | HARDENED
)
def add_output(self, output: CardanoTxOutput) -> None:
if not output.address_parameters:
return
if not output.address_parameters.address_n:
return
self._add(output.address_parameters.address_n, INVALID_OUTPUT)
def add_certificate(self, certificate: CardanoTxCertificate) -> None:
if not certificate.path:
return
self._add(certificate.path, INVALID_CERTIFICATE)
def add_pool_owner(self, pool_owner: CardanoPoolOwner) -> None:
if not pool_owner.staking_key_path:
return
self._add(pool_owner.staking_key_path, INVALID_CERTIFICATE)
def add_withdrawal(self, withdrawal: CardanoTxWithdrawal) -> None:
if not withdrawal.path:
return
self._add(withdrawal.path, INVALID_WITHDRAWAL)
def add_witness_request(self, witness_request: CardanoTxWitnessRequest) -> None:
self._add(witness_request.path, INVALID_WITNESS_REQUEST)