mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-04 05:28:17 +00:00
531 lines
15 KiB
Python
531 lines
15 KiB
Python
from ubinascii import hexlify
|
|
|
|
from trezor import ui
|
|
from trezor.enums import ButtonRequestType, CardanoAddressType, CardanoCertificateType
|
|
from trezor.strings import format_amount
|
|
from trezor.ui.layouts import (
|
|
confirm_metadata,
|
|
confirm_output,
|
|
confirm_path_warning,
|
|
confirm_properties,
|
|
)
|
|
|
|
from apps.common.layout import address_n_to_str
|
|
|
|
from . import seed
|
|
from .address import (
|
|
encode_human_readable_address,
|
|
get_public_key_hash,
|
|
pack_reward_address_bytes,
|
|
)
|
|
from .helpers import protocol_magics
|
|
from .helpers.utils import (
|
|
format_account_number,
|
|
format_asset_fingerprint,
|
|
format_optional_int,
|
|
format_stake_pool_id,
|
|
to_account_path,
|
|
)
|
|
|
|
if False:
|
|
from trezor import wire
|
|
from trezor.messages import (
|
|
CardanoBlockchainPointerType,
|
|
CardanoTxCertificateType,
|
|
CardanoTxWithdrawalType,
|
|
CardanoPoolParametersType,
|
|
CardanoPoolOwnerType,
|
|
CardanoPoolMetadataType,
|
|
CardanoAssetGroupType,
|
|
)
|
|
|
|
|
|
ADDRESS_TYPE_NAMES = {
|
|
CardanoAddressType.BYRON: "Legacy",
|
|
CardanoAddressType.BASE: "Base",
|
|
CardanoAddressType.POINTER: "Pointer",
|
|
CardanoAddressType.ENTERPRISE: "Enterprise",
|
|
CardanoAddressType.REWARD: "Reward",
|
|
}
|
|
|
|
CERTIFICATE_TYPE_NAMES = {
|
|
CardanoCertificateType.STAKE_REGISTRATION: "Stake key registration",
|
|
CardanoCertificateType.STAKE_DEREGISTRATION: "Stake key deregistration",
|
|
CardanoCertificateType.STAKE_DELEGATION: "Stake delegation",
|
|
CardanoCertificateType.STAKE_POOL_REGISTRATION: "Stakepool registration",
|
|
}
|
|
|
|
|
|
def format_coin_amount(amount: int) -> str:
|
|
return "%s %s" % (format_amount(amount, 6), "ADA")
|
|
|
|
|
|
def is_printable_ascii_bytestring(bytestr: bytes) -> bool:
|
|
return all((32 < b < 127) for b in bytestr)
|
|
|
|
|
|
async def confirm_sending(
|
|
ctx: wire.Context,
|
|
ada_amount: int,
|
|
token_bundle: list[CardanoAssetGroupType],
|
|
to: str,
|
|
) -> None:
|
|
await confirm_sending_token_bundle(ctx, token_bundle)
|
|
|
|
await confirm_output(
|
|
ctx,
|
|
to,
|
|
format_coin_amount(ada_amount),
|
|
title="Confirm transaction",
|
|
subtitle="Confirm sending:",
|
|
font_amount=ui.BOLD,
|
|
width_paginated=17,
|
|
to_str="\nto\n",
|
|
to_paginated=True,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_sending_token_bundle(
|
|
ctx: wire.Context, token_bundle: list[CardanoAssetGroupType]
|
|
) -> None:
|
|
for token_group in token_bundle:
|
|
for token in token_group.tokens:
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_token",
|
|
title="Confirm transaction",
|
|
props=[
|
|
(
|
|
"Asset fingerprint:",
|
|
format_asset_fingerprint(
|
|
policy_id=token_group.policy_id,
|
|
asset_name_bytes=token.asset_name_bytes,
|
|
),
|
|
),
|
|
("Amount sent:", format_amount(token.amount, 0)),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None:
|
|
await confirm_metadata(
|
|
ctx,
|
|
"confirm_tokens",
|
|
title="Confirm transaction",
|
|
content="The following\ntransaction output\ncontains tokens.",
|
|
larger_vspace=True,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_path(ctx: wire.Context, path: list[int], title: str) -> None:
|
|
await confirm_path_warning(ctx, address_n_to_str(path), path_type=title)
|
|
|
|
|
|
async def show_warning_tx_no_staking_info(
|
|
ctx: wire.Context, address_type: CardanoAddressType, amount: int
|
|
) -> None:
|
|
atype = ADDRESS_TYPE_NAMES[address_type].lower()
|
|
content = "Change %s address has no stake rights.\nChange amount:\n{}" % atype
|
|
await confirm_metadata(
|
|
ctx,
|
|
"warning_staking",
|
|
title="Confirm transaction",
|
|
content=content,
|
|
param=format_coin_amount(amount),
|
|
hide_continue=True,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_tx_pointer_address(
|
|
ctx: wire.Context,
|
|
pointer: CardanoBlockchainPointerType,
|
|
amount: int,
|
|
) -> None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"warning_pointer",
|
|
title="Confirm transaction",
|
|
props=[
|
|
("Change address has a\npointer with staking\nrights.\n\n\n", None),
|
|
(
|
|
"Pointer:",
|
|
"%s, %s, %s"
|
|
% (
|
|
pointer.block_index,
|
|
pointer.tx_index,
|
|
pointer.certificate_index,
|
|
),
|
|
),
|
|
("Change amount:", format_coin_amount(amount)),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_tx_different_staking_account(
|
|
ctx: wire.Context,
|
|
staking_account_path: list[int],
|
|
amount: int,
|
|
) -> None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"warning_differentstaking",
|
|
title="Confirm transaction",
|
|
props=[
|
|
(
|
|
"Change address staking rights do not match the current account.\n\n",
|
|
None,
|
|
),
|
|
(
|
|
"Staking account %s:" % format_account_number(staking_account_path),
|
|
address_n_to_str(staking_account_path),
|
|
),
|
|
("Change amount:", format_coin_amount(amount)),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_tx_staking_key_hash(
|
|
ctx: wire.Context,
|
|
staking_key_hash: bytes,
|
|
amount: int,
|
|
) -> None:
|
|
props = [
|
|
("Change address staking rights do not match the current account.\n\n", None),
|
|
("Staking key hash:", hexlify(staking_key_hash).decode()),
|
|
("Change amount:", format_coin_amount(amount)),
|
|
]
|
|
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_different_stakingrights",
|
|
title="Confirm transaction",
|
|
props=props,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_transaction(
|
|
ctx: wire.Context,
|
|
amount: int,
|
|
fee: int,
|
|
protocol_magic: int,
|
|
ttl: int | None,
|
|
validity_interval_start: int | None,
|
|
is_network_id_verifiable: bool,
|
|
) -> None:
|
|
props = [
|
|
("Transaction amount:", format_coin_amount(amount)),
|
|
("Transaction fee:", format_coin_amount(fee)),
|
|
]
|
|
|
|
if is_network_id_verifiable:
|
|
props.append(("Network:", protocol_magics.to_ui_string(protocol_magic)))
|
|
|
|
props.append(
|
|
("Valid since: %s" % format_optional_int(validity_interval_start), None)
|
|
)
|
|
props.append(("TTL: %s" % format_optional_int(ttl), None))
|
|
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_total",
|
|
title="Confirm transaction",
|
|
props=props,
|
|
hold=True,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_certificate(
|
|
ctx: wire.Context, certificate: CardanoTxCertificateType
|
|
) -> None:
|
|
# stake pool registration requires custom confirmation logic not covered
|
|
# in this call
|
|
assert certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION
|
|
|
|
props = [
|
|
("Confirm:", CERTIFICATE_TYPE_NAMES[certificate.type]),
|
|
(
|
|
"for account %s:" % format_account_number(certificate.path),
|
|
address_n_to_str(to_account_path(certificate.path)),
|
|
),
|
|
]
|
|
if certificate.type == CardanoCertificateType.STAKE_DELEGATION:
|
|
assert certificate.pool is not None # validate_certificate
|
|
props.append(("to pool:", format_stake_pool_id(certificate.pool)))
|
|
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_certificate",
|
|
title="Confirm transaction",
|
|
props=props,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_parameters(
|
|
ctx: wire.Context,
|
|
pool_parameters: CardanoPoolParametersType,
|
|
network_id: int,
|
|
protocol_magic: int,
|
|
) -> None:
|
|
margin_percentage = (
|
|
100.0 * pool_parameters.margin_numerator / pool_parameters.margin_denominator
|
|
)
|
|
percentage_formatted = ("%f" % margin_percentage).rstrip("0").rstrip(".")
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_pool_registration",
|
|
title="Confirm transaction",
|
|
props=[
|
|
(
|
|
"Stake pool registration\nPool ID:",
|
|
format_stake_pool_id(pool_parameters.pool_id),
|
|
),
|
|
("Pool reward account:", pool_parameters.reward_account),
|
|
(
|
|
"Pledge: {}\nCost: {}\nMargin: {}%".format(
|
|
format_coin_amount(pool_parameters.pledge),
|
|
format_coin_amount(pool_parameters.cost),
|
|
percentage_formatted,
|
|
),
|
|
None,
|
|
),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_owners(
|
|
ctx: wire.Context,
|
|
keychain: seed.Keychain,
|
|
owners: list[CardanoPoolOwnerType],
|
|
network_id: int,
|
|
) -> None:
|
|
props: list[tuple[str, str | None]] = []
|
|
for index, owner in enumerate(owners, 1):
|
|
if owner.staking_key_path:
|
|
props.append(
|
|
("Pool owner #%d:" % index, address_n_to_str(owner.staking_key_path))
|
|
)
|
|
props.append(
|
|
(
|
|
encode_human_readable_address(
|
|
pack_reward_address_bytes(
|
|
get_public_key_hash(keychain, owner.staking_key_path),
|
|
network_id,
|
|
)
|
|
),
|
|
None,
|
|
)
|
|
)
|
|
else:
|
|
assert owner.staking_key_hash is not None # validate_pool_owners
|
|
props.append(
|
|
(
|
|
"Pool owner #%d:" % index,
|
|
encode_human_readable_address(
|
|
pack_reward_address_bytes(owner.staking_key_hash, network_id)
|
|
),
|
|
)
|
|
)
|
|
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_pool_owners",
|
|
title="Confirm transaction",
|
|
props=props,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_metadata(
|
|
ctx: wire.Context,
|
|
metadata: CardanoPoolMetadataType | None,
|
|
) -> None:
|
|
if metadata is None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_pool_metadata",
|
|
title="Confirm transaction",
|
|
props=[("Pool has no metadata (anonymous pool)", None)],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
return
|
|
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_pool_metadata",
|
|
title="Confirm transaction",
|
|
props=[
|
|
("Pool metadata url:", metadata.url),
|
|
("Pool metadata hash:", hexlify(metadata.hash).decode()),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_transaction_network_ttl(
|
|
ctx: wire.Context,
|
|
protocol_magic: int,
|
|
ttl: int | None,
|
|
validity_interval_start: int | None,
|
|
) -> None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_pool_network",
|
|
title="Confirm transaction",
|
|
props=[
|
|
("Network:", protocol_magics.to_ui_string(protocol_magic)),
|
|
(
|
|
"Valid since: %s" % format_optional_int(validity_interval_start),
|
|
None,
|
|
),
|
|
("TTL: %s" % format_optional_int(ttl), None),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_registration_final(
|
|
ctx: wire.Context,
|
|
) -> None:
|
|
await confirm_metadata(
|
|
ctx,
|
|
"confirm_pool_final",
|
|
title="Confirm transaction",
|
|
content="Confirm signing the stake pool registration as an owner",
|
|
hide_continue=True,
|
|
hold=True,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_withdrawal(
|
|
ctx: wire.Context, withdrawal: CardanoTxWithdrawalType
|
|
) -> None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_withdrawal",
|
|
title="Confirm transaction",
|
|
props=[
|
|
(
|
|
"Confirm withdrawal\nfor account %s:"
|
|
% format_account_number(withdrawal.path),
|
|
address_n_to_str(to_account_path(withdrawal.path)),
|
|
),
|
|
("Amount:", format_coin_amount(withdrawal.amount)),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_catalyst_registration(
|
|
ctx: wire.Context,
|
|
public_key: str,
|
|
staking_path: list[int],
|
|
reward_address: str,
|
|
nonce: int,
|
|
) -> None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_catalyst_registration",
|
|
title="Confirm transaction",
|
|
props=[
|
|
("Catalyst voting key registration", None),
|
|
("Voting public key:", public_key),
|
|
(
|
|
"Staking key for account %s:" % format_account_number(staking_path),
|
|
address_n_to_str(staking_path),
|
|
),
|
|
("Rewards go to:", reward_address),
|
|
("Nonce:", str(nonce)),
|
|
],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_auxiliary_data_hash(
|
|
ctx: wire.Context, auxiliary_data_hash: bytes
|
|
) -> None:
|
|
await confirm_properties(
|
|
ctx,
|
|
"confirm_auxiliary_data",
|
|
title="Confirm transaction",
|
|
props=[("Auxiliary data hash:", hexlify(auxiliary_data_hash).decode())],
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_address_foreign_staking_key(
|
|
ctx: wire.Context,
|
|
account_path: list[int],
|
|
staking_account_path: list[int],
|
|
staking_key_hash: bytes | None,
|
|
) -> None:
|
|
props: list[tuple[str, str | None]] = [
|
|
(
|
|
"Stake rights associated with this address do not match your account %s:"
|
|
% format_account_number(account_path),
|
|
address_n_to_str(account_path),
|
|
)
|
|
]
|
|
|
|
if staking_account_path:
|
|
props.append(
|
|
(
|
|
"Stake account %s:" % format_account_number(staking_account_path),
|
|
address_n_to_str(staking_account_path),
|
|
)
|
|
)
|
|
else:
|
|
assert staking_key_hash is not None # _validate_base_address_staking_info
|
|
props.append(("Staking key:", hexlify(staking_key_hash).decode()))
|
|
props.append(("Continue?", None))
|
|
|
|
await confirm_properties(
|
|
ctx,
|
|
"warning_foreign_stakingkey",
|
|
title="Warning",
|
|
props=props,
|
|
icon=ui.ICON_WRONG,
|
|
icon_color=ui.RED,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_tx_network_unverifiable(ctx: wire.Context) -> None:
|
|
await confirm_metadata(
|
|
ctx,
|
|
"warning_no_outputs",
|
|
title="Warning",
|
|
content="Transaction has no outputs, network cannot be verified.",
|
|
larger_vspace=True,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_warning_address_pointer(
|
|
ctx: wire.Context, pointer: CardanoBlockchainPointerType
|
|
) -> None:
|
|
content = "Pointer address:\nBlock: %s\nTransaction: %s\nCertificate: %s" % (
|
|
pointer.block_index,
|
|
pointer.tx_index,
|
|
pointer.certificate_index,
|
|
)
|
|
await confirm_metadata(
|
|
ctx,
|
|
"warning_pointer",
|
|
title="Warning",
|
|
icon=ui.ICON_WRONG,
|
|
icon_color=ui.RED,
|
|
content=content,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|