1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-16 17:42:02 +00:00

cardano better address export,sign tx UX,format fix, prot. magic instead of network

This commit is contained in:
refi93 2018-12-15 01:59:42 +01:00
parent ea775c2750
commit 2b2e2d0145
5 changed files with 130 additions and 80 deletions

View File

@ -1,10 +1,10 @@
from trezor import log, ui, wire from trezor import log, wire
from trezor.messages.CardanoAddress import CardanoAddress from trezor.messages.CardanoAddress import CardanoAddress
from apps.cardano import seed from apps.cardano import seed
from apps.cardano.address import derive_address_and_node, validate_full_path from apps.cardano.address import derive_address_and_node, validate_full_path
from apps.cardano.layout import confirm_with_pagination
from apps.common import paths from apps.common import paths
from apps.common.layout import address_n_to_str, show_address, show_qr
async def get_address(ctx, msg): async def get_address(ctx, msg):
@ -18,11 +18,12 @@ async def get_address(ctx, msg):
if __debug__: if __debug__:
log.exception(__name__, e) log.exception(__name__, e)
raise wire.ProcessError("Deriving address failed") raise wire.ProcessError("Deriving address failed")
if msg.show_display: if msg.show_display:
if not await confirm_with_pagination( desc = address_n_to_str(msg.address_n)
ctx, address, "Export address", icon=ui.ICON_SEND, icon_color=ui.GREEN while True:
): if await show_address(ctx, address, desc=desc):
raise wire.ActionCancelled("Exporting cancelled") break
if await show_qr(ctx, address, desc=desc):
break
return CardanoAddress(address=address) return CardanoAddress(address=address)

View File

@ -3,10 +3,77 @@ from micropython import const
from trezor import ui from trezor import ui
from trezor.messages import ButtonRequestType, MessageType from trezor.messages import ButtonRequestType, MessageType
from trezor.messages.ButtonRequest import ButtonRequest from trezor.messages.ButtonRequest import ButtonRequest
from trezor.ui.confirm import CONFIRMED, ConfirmDialog from trezor.ui.confirm import CONFIRMED, ConfirmDialog, HoldToConfirmDialog
from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.ui.scroll import Scrollpage, animate_swipe, paginate
from trezor.ui.text import Text from trezor.ui.text import Text
from trezor.utils import chunks from trezor.utils import chunks, format_amount
def format_coin_amount(amount):
return "%s %s" % (format_amount(amount, 6), "ADA")
async def confirm_sending(ctx, amount, to):
to_lines = list(chunks(to, 17))
t1 = Text("Confirm transaction", ui.ICON_SEND, icon_color=ui.GREEN)
t1.normal("Confirm sending:")
t1.bold(format_coin_amount(amount))
t1.normal("to:")
t1.bold(to_lines[0])
pages = [t1]
LINES_PER_PAGE = 4
if len(to_lines) > 1:
to_pages = list(chunks(to_lines[1:], LINES_PER_PAGE))
for page in to_pages:
t = Text("Confirm transaction", ui.ICON_SEND, icon_color=ui.GREEN)
for line in page:
t.bold(line)
pages.append(t)
await ctx.call(ButtonRequest(code=ButtonRequestType.Other), MessageType.ButtonAck)
paginator = paginate(create_renderer(ConfirmDialog), len(pages), const(0), pages)
return await ctx.wait(paginator) == CONFIRMED
async def confirm_transaction(ctx, amount, fee, network_name):
t1 = Text("Confirm transaction", ui.ICON_SEND, icon_color=ui.GREEN)
t1.normal("Total amount:")
t1.bold(format_coin_amount(amount))
t1.normal("including fee:")
t1.bold(format_coin_amount(fee))
t2 = Text("Confirm transaction", ui.ICON_SEND, icon_color=ui.GREEN)
t2.normal("Network:")
t2.bold(network_name)
pages = [t1, t2]
await ctx.call(ButtonRequest(code=ButtonRequestType.Other), MessageType.ButtonAck)
paginator = paginate(
create_renderer(HoldToConfirmDialog), len(pages), const(0), pages
)
return await ctx.wait(paginator) == CONFIRMED
def create_renderer(confirmation_wrapper):
@ui.layout
async def page_renderer(page: int, page_count: int, pages: list):
# for some reason page index can be equal to page count
if page >= page_count:
page = page_count - 1
content = Scrollpage(pages[page], page, page_count)
if page + 1 >= page_count:
return await confirmation_wrapper(content)
else:
content.render()
await animate_swipe()
return page_renderer
async def confirm_with_pagination( async def confirm_with_pagination(

View File

@ -1,74 +1,59 @@
from trezor import log, ui, wire from micropython import const
from trezor import log, wire
from trezor.crypto import base58, hashlib from trezor.crypto import base58, hashlib
from trezor.crypto.curve import ed25519 from trezor.crypto.curve import ed25519
from trezor.messages.CardanoSignedTx import CardanoSignedTx from trezor.messages.CardanoSignedTx import CardanoSignedTx
from trezor.messages.CardanoTxRequest import CardanoTxRequest from trezor.messages.CardanoTxRequest import CardanoTxRequest
from trezor.messages.MessageType import CardanoTxAck from trezor.messages.MessageType import CardanoTxAck
from trezor.ui.text import BR
from apps.cardano import cbor, seed from apps.cardano import cbor, seed
from apps.cardano.address import derive_address_and_node, validate_full_path from apps.cardano.address import derive_address_and_node, validate_full_path
from apps.cardano.layout import confirm_with_pagination, progress from apps.cardano.layout import confirm_sending, confirm_transaction, progress
from apps.common.layout import address_n_to_str, split_address
from apps.common.paths import validate_path from apps.common.paths import validate_path
from apps.common.seed import remove_ed25519_prefix from apps.common.seed import remove_ed25519_prefix
from apps.homescreen.homescreen import display_homescreen from apps.homescreen.homescreen import display_homescreen
# the maximum allowed change address. this should be large enough for normal
# use and still allow to quickly brute-force the correct bip32 path
MAX_CHANGE_ADDRESS_INDEX = const(1000000)
ACCOUNT_PREFIX_DEPTH = const(2)
KNOWN_PROTOCOL_MAGICS = {764824073: "Mainnet", 1097911063: "Testnet"}
# we consider addresses from the external chain as possible change addresses as well
def is_change(output, inputs):
for input in inputs:
inp = input.address_n
if (
not output[:ACCOUNT_PREFIX_DEPTH] == inp[:ACCOUNT_PREFIX_DEPTH]
or not output[-2] < 2
or not output[-1] < MAX_CHANGE_ADDRESS_INDEX
):
return False
return True
async def show_tx( async def show_tx(
ctx, ctx,
outputs: list, outputs: list,
outcoins: list, outcoins: list,
change_derivation_paths: list, fee: int,
change_coins: list,
fee: float,
tx_size: float,
network_name: str, network_name: str,
raw_inputs: list,
raw_outputs: list,
) -> bool: ) -> bool:
lines = ("%s ADA" % _micro_ada_to_ada(fee), BR, "Tx size:", "%s bytes" % tx_size)
if not await confirm_with_pagination(
ctx, lines, "Confirm fee", ui.ICON_SEND, ui.GREEN
):
return False
if not await confirm_with_pagination(
ctx, "%s network" % network_name, "Confirm network", ui.ICON_SEND, ui.GREEN
):
return False
for index, output in enumerate(outputs): for index, output in enumerate(outputs):
if not await confirm_with_pagination( if is_change(raw_outputs[index].address_n, raw_inputs):
ctx, output, "Confirm output", ui.ICON_SEND, ui.GREEN continue
):
if not await confirm_sending(ctx, outcoins[index], output):
return False return False
if not await confirm_with_pagination( total_amount = sum(outcoins)
ctx, if not await confirm_transaction(ctx, total_amount, fee, network_name):
"%s ADA" % _micro_ada_to_ada(outcoins[index]), return False
"Confirm amount",
ui.ICON_SEND,
ui.GREEN,
):
return False
for index, change in enumerate(change_derivation_paths):
if not await confirm_with_pagination(
ctx,
list(split_address(address_n_to_str(change))),
"Confirm change",
ui.ICON_SEND,
ui.GREEN,
):
return False
if not await confirm_with_pagination(
ctx,
"%s ADA" % _micro_ada_to_ada(change_coins[index]),
"Confirm amount",
ui.ICON_SEND,
ui.GREEN,
):
return False
return True return True
@ -100,7 +85,7 @@ async def sign_tx(ctx, msg):
# sign the transaction bundle and prepare the result # sign the transaction bundle and prepare the result
transaction = Transaction( transaction = Transaction(
msg.inputs, msg.outputs, transactions, keychain, msg.network msg.inputs, msg.outputs, transactions, keychain, msg.protocol_magic
) )
tx_body, tx_hash = transaction.serialise_tx() tx_body, tx_hash = transaction.serialise_tx()
tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash) tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash)
@ -115,24 +100,24 @@ async def sign_tx(ctx, msg):
ctx, ctx,
transaction.output_addresses, transaction.output_addresses,
transaction.outgoing_coins, transaction.outgoing_coins,
transaction.change_derivation_paths,
transaction.change_coins,
transaction.fee, transaction.fee,
len(tx_body),
transaction.network_name, transaction.network_name,
transaction.inputs,
transaction.outputs,
): ):
raise wire.ActionCancelled("Signing cancelled") raise wire.ActionCancelled("Signing cancelled")
return tx return tx
def _micro_ada_to_ada(amount: float) -> float:
return amount / 1000000
class Transaction: class Transaction:
def __init__( def __init__(
self, inputs: list, outputs: list, transactions: list, keychain, network: int self,
inputs: list,
outputs: list,
transactions: list,
keychain,
protocol_magic: int,
): ):
self.inputs = inputs self.inputs = inputs
self.outputs = outputs self.outputs = outputs
@ -140,14 +125,9 @@ class Transaction:
self.keychain = keychain self.keychain = keychain
# attributes have to be always empty in current Cardano # attributes have to be always empty in current Cardano
self.attributes = {} self.attributes = {}
if network == 1:
self.network_name = "Testnet" self.network_name = KNOWN_PROTOCOL_MAGICS.get(protocol_magic, "Unknown")
self.network_magic = b"\x01\x1a\x41\x70\xcb\x17\x58\x20" self.protocol_magic = protocol_magic
elif network == 2:
self.network_name = "Mainnet"
self.network_magic = b"\x01\x1a\x2d\x96\x4a\x09\x58\x20"
else:
raise wire.ProcessError("Unknown network index %d" % network)
def _process_inputs(self): def _process_inputs(self):
input_coins = [] input_coins = []
@ -217,7 +197,9 @@ class Transaction:
def _build_witnesses(self, tx_aux_hash: str): def _build_witnesses(self, tx_aux_hash: str):
witnesses = [] witnesses = []
for index, node in enumerate(self.nodes): for index, node in enumerate(self.nodes):
message = self.network_magic + tx_aux_hash message = (
b"\x01" + cbor.encode(self.protocol_magic) + b"\x58\x20" + tx_aux_hash
)
signature = ed25519.sign_ext( signature = ed25519.sign_ext(
node.private_key(), node.private_key_ext(), message node.private_key(), node.private_key_ext(), message
) )

View File

@ -20,12 +20,12 @@ class CardanoSignTx(p.MessageType):
inputs: List[CardanoTxInputType] = None, inputs: List[CardanoTxInputType] = None,
outputs: List[CardanoTxOutputType] = None, outputs: List[CardanoTxOutputType] = None,
transactions_count: int = None, transactions_count: int = None,
network: int = None, protocol_magic: int = None,
) -> None: ) -> None:
self.inputs = inputs if inputs is not None else [] self.inputs = inputs if inputs is not None else []
self.outputs = outputs if outputs is not None else [] self.outputs = outputs if outputs is not None else []
self.transactions_count = transactions_count self.transactions_count = transactions_count
self.network = network self.protocol_magic = protocol_magic
@classmethod @classmethod
def get_fields(cls): def get_fields(cls):
@ -33,5 +33,5 @@ class CardanoSignTx(p.MessageType):
1: ('inputs', CardanoTxInputType, p.FLAG_REPEATED), 1: ('inputs', CardanoTxInputType, p.FLAG_REPEATED),
2: ('outputs', CardanoTxOutputType, p.FLAG_REPEATED), 2: ('outputs', CardanoTxOutputType, p.FLAG_REPEATED),
3: ('transactions_count', p.UVarintType, 0), 3: ('transactions_count', p.UVarintType, 0),
4: ('network', p.UVarintType, 0), 5: ('protocol_magic', p.UVarintType, 0),
} }

@ -1 +1 @@
Subproject commit 877778fc93db87544df64e4fc1fa91deee1c8334 Subproject commit b947aa3d45af58605f8f090078a2de3d5878571d