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/stellar/operations/layout.py

287 lines
8.2 KiB

from trezor.enums import StellarAssetType, StellarSignerType
from trezor.messages import (
StellarAccountMergeOp,
StellarAllowTrustOp,
StellarAsset,
StellarBumpSequenceOp,
StellarChangeTrustOp,
StellarCreateAccountOp,
StellarCreatePassiveOfferOp,
StellarManageDataOp,
StellarManageOfferOp,
StellarPathPaymentOp,
StellarPaymentOp,
StellarSetOptionsOp,
)
from trezor.ui.layouts import (
confirm_address,
confirm_amount,
confirm_blob,
confirm_metadata,
confirm_output,
confirm_properties,
confirm_text,
)
from trezor.wire import DataError, ProcessError
from .. import consts, helpers
from ..layout import format_amount, format_asset
if False:
from trezor.wire import Context
async def confirm_source_account(ctx: Context, source_account: str) -> None:
await confirm_address(
ctx,
"Confirm operation",
source_account,
description="Source account:",
br_type="op_source_account",
)
async def confirm_allow_trust_op(ctx: Context, op: StellarAllowTrustOp) -> None:
await confirm_properties(
ctx,
"op_allow_trust",
title="Allow trust" if op.is_authorized else "Revoke trust",
props=(
("Asset", op.asset_code),
("Trusted Account", op.trusted_account),
),
)
async def confirm_account_merge_op(ctx: Context, op: StellarAccountMergeOp) -> None:
await confirm_address(
ctx,
"Account Merge",
op.destination_account,
description="All XLM will be sent to:",
br_type="op_account_merge",
)
async def confirm_bump_sequence_op(ctx: Context, op: StellarBumpSequenceOp) -> None:
await confirm_metadata(
ctx,
"op_bump",
"Bump Sequence",
content="Set sequence to {}?",
param=str(op.bump_to),
)
async def confirm_change_trust_op(ctx: Context, op: StellarChangeTrustOp) -> None:
await confirm_amount(
ctx,
title="Delete trust" if op.limit == 0 else "Add trust",
amount=format_amount(op.limit, op.asset),
description="Limit:",
br_type="op_change_trust",
)
await confirm_asset_issuer(ctx, op.asset)
async def confirm_create_account_op(ctx: Context, op: StellarCreateAccountOp) -> None:
await confirm_properties(
ctx,
"op_create_account",
"Create Account",
props=(
("Account", op.new_account),
("Initial Balance", format_amount(op.starting_balance)),
),
)
async def confirm_create_passive_offer_op(
ctx: Context, op: StellarCreatePassiveOfferOp
) -> None:
if op.amount == 0:
text = "Delete Passive Offer"
else:
text = "New Passive Offer"
await _confirm_offer(ctx, text, op)
async def confirm_manage_offer_op(ctx: Context, op: StellarManageOfferOp) -> None:
if op.offer_id == 0:
text = "New Offer"
else:
if op.amount == 0:
text = "Delete"
else:
text = "Update"
text += f" #{op.offer_id}"
await _confirm_offer(ctx, text, op)
async def _confirm_offer(
ctx: Context,
title: str,
op: StellarCreatePassiveOfferOp | StellarManageOfferOp,
) -> None:
await confirm_properties(
ctx,
"op_offer",
title=title,
props=(
("Selling:", format_amount(op.amount, op.selling_asset)),
("Buying:", format_asset(op.buying_asset)),
(
f"Price per {format_asset(op.buying_asset)}:",
str(op.price_n / op.price_d),
),
),
)
await confirm_asset_issuer(ctx, op.selling_asset)
await confirm_asset_issuer(ctx, op.buying_asset)
async def confirm_manage_data_op(ctx: Context, op: StellarManageDataOp) -> None:
from trezor.crypto.hashlib import sha256
if op.value:
digest = sha256(op.value).digest()
await confirm_properties(
ctx,
"op_data",
"Set data",
props=(("Key:", op.key), ("Value (SHA-256):", digest)),
)
else:
await confirm_metadata(
ctx,
"op_data",
"Clear data",
"Do you want to clear value key {}?",
param=op.key,
)
async def confirm_path_payment_op(ctx: Context, op: StellarPathPaymentOp) -> None:
await confirm_output(
ctx,
address=op.destination_account,
amount=format_amount(op.destination_amount, op.destination_asset),
title="Path Pay",
)
await confirm_asset_issuer(ctx, op.destination_asset)
# confirm what the sender is using to pay
await confirm_amount(
ctx,
title="Debited amount",
amount=format_amount(op.send_max, op.send_asset),
description="Pay at most:",
br_type="op_path_payment",
)
await confirm_asset_issuer(ctx, op.send_asset)
async def confirm_payment_op(ctx: Context, op: StellarPaymentOp) -> None:
await confirm_output(
ctx,
address=op.destination_account,
amount=format_amount(op.amount, op.asset),
)
await confirm_asset_issuer(ctx, op.asset)
async def confirm_set_options_op(ctx: Context, op: StellarSetOptionsOp) -> None:
if op.inflation_destination_account:
await confirm_address(
ctx,
"Inflation",
op.inflation_destination_account,
description="Destination:",
br_type="op_inflation",
)
if op.clear_flags:
t = _format_flags(op.clear_flags)
await confirm_text(ctx, "op_set_options", "Clear flags", data=t)
if op.set_flags:
t = _format_flags(op.set_flags)
await confirm_text(ctx, "op_set_options", "Set flags", data=t)
thresholds = _format_thresholds(op)
if thresholds:
await confirm_properties(
ctx, "op_thresholds", "Account Thresholds", props=thresholds
)
if op.home_domain:
await confirm_text(ctx, "op_home_domain", "Home Domain", op.home_domain)
if op.signer_type is not None:
if op.signer_key is None or op.signer_weight is None:
raise DataError("Stellar: invalid signer option data.")
if op.signer_weight > 0:
title = "Add Signer"
else:
title = "Remove Signer"
data: str | bytes = ""
if op.signer_type == StellarSignerType.ACCOUNT:
description = "Account:"
data = helpers.address_from_public_key(op.signer_key)
elif op.signer_type == StellarSignerType.PRE_AUTH:
description = "Pre-auth transaction:"
data = op.signer_key
elif op.signer_type == StellarSignerType.HASH:
description = "Hash:"
data = op.signer_key
else:
raise ProcessError("Stellar: invalid signer type")
await confirm_blob(
ctx,
"op_signer",
title=title,
description=description,
data=data,
)
def _format_thresholds(op: StellarSetOptionsOp) -> list[tuple[str, str]]:
props = []
if op.master_weight is not None:
props.append(("Master Weight:", str(op.master_weight)))
if op.low_threshold is not None:
props.append(("Low:", str(op.low_threshold)))
if op.medium_threshold is not None:
props.append(("Medium:", str(op.medium_threshold)))
if op.high_threshold is not None:
props.append(("High:", str(op.high_threshold)))
return props
def _format_flags(flags: int) -> str:
if flags > consts.FLAGS_MAX_SIZE:
raise ProcessError("Stellar: invalid flags")
flags_set = []
if flags & consts.FLAG_AUTH_REQUIRED:
flags_set.append("AUTH_REQUIRED\n")
if flags & consts.FLAG_AUTH_REVOCABLE:
flags_set.append("AUTH_REVOCABLE\n")
if flags & consts.FLAG_AUTH_IMMUTABLE:
flags_set.append("AUTH_IMMUTABLE\n")
return "".join(flags_set)
async def confirm_asset_issuer(ctx: Context, asset: StellarAsset) -> None:
if asset.type == StellarAssetType.NATIVE:
return
if asset.issuer is None or asset.code is None:
raise DataError("Stellar: invalid asset definition")
await confirm_address(
ctx,
"Confirm Issuer",
asset.issuer,
description=f"{asset.code} issuer:",
br_type="confirm_asset_issuer",
)