feat(cardano): add support for hiding some details during tx signing

pull/2374/head
gabrielkerekes 2 years ago committed by matejcik
parent 2f9435570d
commit 1a68650c43

@ -313,6 +313,10 @@ Note that including `inline_datum` and/or `reference_script` requires using the
When one wants to spend funds from a Plutus script address (which is possible only in the Plutus signing mode), they have to attach the Plutus script body as well as the datum referenced in the UTXO (if they did not utilize `inline_datum` and `reference_script` with `reference_inputs`). A redeemer must be provided too. These items are outside the transaction body which is signed by Trezor, so their hash is included in the transaction body as `script_data_hash`. When one wants to spend funds from a Plutus script address (which is possible only in the Plutus signing mode), they have to attach the Plutus script body as well as the datum referenced in the UTXO (if they did not utilize `inline_datum` and `reference_script` with `reference_inputs`). A redeemer must be provided too. These items are outside the transaction body which is signed by Trezor, so their hash is included in the transaction body as `script_data_hash`.
### Level of details ("Show All"/"Show Simple")
With the introduction of Plutus scripts the transaction signing UI has become a pain for the users. There are many elements which an ordinary user has no chance to verify and they might only confuse and bother him during the transaction signing process. This is why we have introduced the option to hide some transaction elements by letting the user choose the level of details for the transaction signing. This is done by adding an initial screen to the transaction signing flow on which two buttons are displayed: "Show All" button to display all transaction details and a "Show Simple" button to only show the critical transaction details.
### Transaction Explorer ### Transaction Explorer
[Cardano explorer](https://explorer.cardano.org/en.html), [Cardanoscan](https://cardanoscan.io/). [Cardano explorer](https://explorer.cardano.org/en.html), [Cardanoscan](https://cardanoscan.io/).

@ -80,6 +80,7 @@ async def show(
| None, | None,
protocol_magic: int, protocol_magic: int,
network_id: int, network_id: int,
should_show_details: bool,
) -> None: ) -> None:
if catalyst_registration_parameters: if catalyst_registration_parameters:
await _show_catalyst_registration( await _show_catalyst_registration(
@ -90,7 +91,8 @@ async def show(
network_id, network_id,
) )
await show_auxiliary_data_hash(ctx, auxiliary_data_hash) if should_show_details:
await show_auxiliary_data_hash(ctx, auxiliary_data_hash)
async def _show_catalyst_registration( async def _show_catalyst_registration(

@ -81,3 +81,7 @@ class OrdinarySigner(Signer):
await layout.confirm_witness_request(self.ctx, witness_path) await layout.confirm_witness_request(self.ctx, witness_path)
elif not is_payment and not is_staking: elif not is_payment and not is_staking:
await self._fail_or_warn_path(witness_path, WITNESS_PATH_NAME) await self._fail_or_warn_path(witness_path, WITNESS_PATH_NAME)
else:
await self._show_if_showing_details(
layout.confirm_witness_request(self.ctx, witness_path)
)

@ -54,7 +54,7 @@ class PlutusSigner(Signer):
async def _show_input(self, input: messages.CardanoTxInput) -> None: async def _show_input(self, input: messages.CardanoTxInput) -> None:
# super() omitted intentionally # super() omitted intentionally
# The inputs are not interchangeable (because of datums), so we must show them. # The inputs are not interchangeable (because of datums), so we must show them.
await layout.confirm_input(self.ctx, input) await self._show_if_showing_details(layout.confirm_input(self.ctx, input))
async def _show_output_credentials( async def _show_output_credentials(
self, address_parameters: messages.CardanoAddressParametersType self, address_parameters: messages.CardanoAddressParametersType
@ -74,6 +74,10 @@ class PlutusSigner(Signer):
# super() omitted intentionally # super() omitted intentionally
# All outputs need to be shown (even device-owned), because they might influence # All outputs need to be shown (even device-owned), because they might influence
# the script evaluation. # the script evaluation.
if self._is_simple_change_output(output):
# only display simple change outputs if showing details
return self.should_show_details
return True return True
def _is_change_output(self, output: messages.CardanoTxOutput) -> bool: def _is_change_output(self, output: messages.CardanoTxOutput) -> bool:

@ -104,11 +104,6 @@ class Signer:
self.msg = msg self.msg = msg
self.keychain = keychain self.keychain = keychain
# Some data (e.g. output inline datum) are too long to verify manually.
# We track their presence and eventually display the tx hash when
# confirming the tx.
self.has_hidden_data = False
self.account_path_checker = AccountPathChecker() self.account_path_checker = AccountPathChecker()
# Inputs, outputs and fee are mandatory, count the number of optional fields present. # Inputs, outputs and fee are mandatory, count the number of optional fields present.
@ -259,8 +254,8 @@ class Signer:
raise NotImplementedError raise NotImplementedError
def _should_show_tx_hash(self) -> bool: def _should_show_tx_hash(self) -> bool:
# By default, we display tx hash only if some data wasn't shown. # By default we display tx hash only if showing details
return self.has_hidden_data return self.should_show_details
# inputs # inputs
@ -407,12 +402,6 @@ class Signer:
Determines whether the output should be shown. Extracted from _show_output Determines whether the output should be shown. Extracted from _show_output
because of readability. because of readability.
""" """
if (
output.datum_hash is not None
or output.inline_datum_size > 0
or output.reference_script_size > 0
):
return True
address_type = self._get_output_address_type(output) address_type = self._get_output_address_type(output)
if ( if (
@ -424,8 +413,13 @@ class Signer:
return True return True
if self._is_simple_change_output(output): if self._is_simple_change_output(output):
# We don't need to display simple address outputs. # Show change output only if showing details and if it contains plutus data
return False has_plutus_data = (
output.datum_hash is not None
or output.inline_datum_size > 0
or output.reference_script_size > 0
)
return self.should_show_details and has_plutus_data
return True return True
@ -459,7 +453,9 @@ class Signer:
if output.datum_hash is not None: if output.datum_hash is not None:
if should_show: if should_show:
await layout.confirm_datum_hash(self.ctx, output.datum_hash) await self._show_if_showing_details(
layout.confirm_datum_hash(self.ctx, output.datum_hash)
)
output_list.append(output.datum_hash) output_list.append(output.datum_hash)
async def _process_babbage_output( async def _process_babbage_output(
@ -486,7 +482,9 @@ class Signer:
if output.datum_hash is not None: if output.datum_hash is not None:
if should_show: if should_show:
await layout.confirm_datum_hash(self.ctx, output.datum_hash) await self._show_if_showing_details(
layout.confirm_datum_hash(self.ctx, output.datum_hash)
)
output_dict.add( output_dict.add(
BABBAGE_OUTPUT_KEY_DATUM_OPTION, BABBAGE_OUTPUT_KEY_DATUM_OPTION,
(DATUM_OPTION_KEY_HASH, output.datum_hash), (DATUM_OPTION_KEY_HASH, output.datum_hash),
@ -626,7 +624,6 @@ class Signer:
should_show: bool, should_show: bool,
) -> None: ) -> None:
assert inline_datum_size > 0 assert inline_datum_size > 0
self.has_hidden_data = True
chunks_count = self._get_chunks_count(inline_datum_size) chunks_count = self._get_chunks_count(inline_datum_size)
for chunk_number in range(chunks_count): for chunk_number in range(chunks_count):
@ -640,8 +637,8 @@ class Signer:
wire.ProcessError("Invalid inline datum chunk"), wire.ProcessError("Invalid inline datum chunk"),
) )
if chunk_number == 0 and should_show: if chunk_number == 0 and should_show:
await layout.confirm_inline_datum( await self._show_if_showing_details(
self.ctx, chunk.data, inline_datum_size layout.confirm_inline_datum(self.ctx, chunk.data, inline_datum_size)
) )
inline_datum_cbor.add(chunk.data) inline_datum_cbor.add(chunk.data)
@ -654,7 +651,6 @@ class Signer:
should_show: bool, should_show: bool,
) -> None: ) -> None:
assert reference_script_size > 0 assert reference_script_size > 0
self.has_hidden_data = True
chunks_count = self._get_chunks_count(reference_script_size) chunks_count = self._get_chunks_count(reference_script_size)
for chunk_number in range(chunks_count): for chunk_number in range(chunks_count):
@ -668,8 +664,10 @@ class Signer:
wire.ProcessError("Invalid reference script chunk"), wire.ProcessError("Invalid reference script chunk"),
) )
if chunk_number == 0 and should_show: if chunk_number == 0 and should_show:
await layout.confirm_reference_script( await self._show_if_showing_details(
self.ctx, chunk.data, reference_script_size layout.confirm_reference_script(
self.ctx, chunk.data, reference_script_size
)
) )
reference_script_cbor.add(chunk.data) reference_script_cbor.add(chunk.data)
@ -803,8 +801,10 @@ class Signer:
) )
self._validate_withdrawal(withdrawal) self._validate_withdrawal(withdrawal)
address_bytes = self._derive_withdrawal_address_bytes(withdrawal) address_bytes = self._derive_withdrawal_address_bytes(withdrawal)
await layout.confirm_withdrawal( await self._show_if_showing_details(
self.ctx, withdrawal, address_bytes, self.msg.network_id layout.confirm_withdrawal(
self.ctx, withdrawal, address_bytes, self.msg.network_id
)
) )
withdrawals_dict.add(address_bytes, withdrawal.amount) withdrawals_dict.add(address_bytes, withdrawal.amount)
@ -842,6 +842,7 @@ class Signer:
data.catalyst_registration_parameters, data.catalyst_registration_parameters,
self.msg.protocol_magic, self.msg.protocol_magic,
self.msg.network_id, self.msg.network_id,
self.should_show_details,
) )
self.tx_dict.add(TX_BODY_KEY_AUXILIARY_DATA, auxiliary_data_hash) self.tx_dict.add(TX_BODY_KEY_AUXILIARY_DATA, auxiliary_data_hash)
@ -897,7 +898,9 @@ class Signer:
async def _process_script_data_hash(self) -> None: async def _process_script_data_hash(self) -> None:
assert self.msg.script_data_hash is not None assert self.msg.script_data_hash is not None
self._validate_script_data_hash() self._validate_script_data_hash()
await layout.confirm_script_data_hash(self.ctx, self.msg.script_data_hash) await self._show_if_showing_details(
layout.confirm_script_data_hash(self.ctx, self.msg.script_data_hash)
)
self.tx_dict.add(TX_BODY_KEY_SCRIPT_DATA_HASH, self.msg.script_data_hash) self.tx_dict.add(TX_BODY_KEY_SCRIPT_DATA_HASH, self.msg.script_data_hash)
def _validate_script_data_hash(self) -> None: def _validate_script_data_hash(self) -> None:
@ -930,7 +933,9 @@ class Signer:
self, collateral_input: messages.CardanoTxCollateralInput self, collateral_input: messages.CardanoTxCollateralInput
) -> None: ) -> None:
if self.msg.total_collateral is None: if self.msg.total_collateral is None:
await layout.confirm_collateral_input(self.ctx, collateral_input) await self._show_if_showing_details(
layout.confirm_collateral_input(self.ctx, collateral_input)
)
# required signers # required signers
@ -942,7 +947,9 @@ class Signer:
messages.CardanoTxItemAck(), messages.CardanoTxRequiredSigner messages.CardanoTxItemAck(), messages.CardanoTxRequiredSigner
) )
self._validate_required_signer(required_signer) self._validate_required_signer(required_signer)
await layout.confirm_required_signer(self.ctx, required_signer) await self._show_if_showing_details(
layout.confirm_required_signer(self.ctx, required_signer)
)
key_hash = required_signer.key_hash or get_public_key_hash( key_hash = required_signer.key_hash or get_public_key_hash(
self.keychain, required_signer.key_path self.keychain, required_signer.key_path
@ -1064,7 +1071,7 @@ class Signer:
if self._is_simple_change_output(output): if self._is_simple_change_output(output):
return False return False
return True return self.should_show_details
# reference inputs # reference inputs
@ -1076,7 +1083,9 @@ class Signer:
messages.CardanoTxItemAck(), messages.CardanoTxReferenceInput messages.CardanoTxItemAck(), messages.CardanoTxReferenceInput
) )
self._validate_reference_input(reference_input) self._validate_reference_input(reference_input)
await layout.confirm_reference_input(self.ctx, reference_input) await self._show_if_showing_details(
layout.confirm_reference_input(self.ctx, reference_input)
)
reference_inputs_list.append( reference_inputs_list.append(
(reference_input.prev_hash, reference_input.prev_index) (reference_input.prev_hash, reference_input.prev_index)
) )
@ -1244,3 +1253,7 @@ class Signer:
if Credential.stake_credential(address_parameters).is_unusual_path: if Credential.stake_credential(address_parameters).is_unusual_path:
raise wire.DataError(f"Invalid {CHANGE_OUTPUT_STAKING_PATH_NAME.lower()}") raise wire.DataError(f"Invalid {CHANGE_OUTPUT_STAKING_PATH_NAME.lower()}")
async def _show_if_showing_details(self, layout_fn: Awaitable) -> None:
if self.should_show_details:
await layout_fn

Loading…
Cancel
Save