|
|
|
@ -1,54 +1,34 @@
|
|
|
|
|
from micropython import const
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
|
|
|
|
from trezor import messages, wire
|
|
|
|
|
from trezor.crypto import hashlib
|
|
|
|
|
from trezor.crypto.curve import ed25519
|
|
|
|
|
from trezor import messages
|
|
|
|
|
from trezor.enums import (
|
|
|
|
|
CardanoAddressType,
|
|
|
|
|
CardanoCertificateType,
|
|
|
|
|
CardanoTxOutputSerializationFormat,
|
|
|
|
|
CardanoTxWitnessType,
|
|
|
|
|
)
|
|
|
|
|
from trezor.messages import CardanoTxItemAck, CardanoTxOutput
|
|
|
|
|
from trezor.wire import DataError, ProcessError
|
|
|
|
|
|
|
|
|
|
from apps.common import cbor, safety_checks
|
|
|
|
|
from apps.common import safety_checks
|
|
|
|
|
|
|
|
|
|
from .. import addresses, auxiliary_data, certificates, layout, seed
|
|
|
|
|
from ..helpers import (
|
|
|
|
|
ADDRESS_KEY_HASH_SIZE,
|
|
|
|
|
INPUT_PREV_HASH_SIZE,
|
|
|
|
|
LOVELACE_MAX_SUPPLY,
|
|
|
|
|
OUTPUT_DATUM_HASH_SIZE,
|
|
|
|
|
SCRIPT_DATA_HASH_SIZE,
|
|
|
|
|
)
|
|
|
|
|
from ..helpers.account_path_check import AccountPathChecker
|
|
|
|
|
from ..helpers.credential import Credential, should_show_credentials
|
|
|
|
|
from ..helpers.hash_builder_collection import (
|
|
|
|
|
HashBuilderDict,
|
|
|
|
|
HashBuilderEmbeddedCBOR,
|
|
|
|
|
HashBuilderList,
|
|
|
|
|
)
|
|
|
|
|
from ..helpers.paths import (
|
|
|
|
|
CERTIFICATE_PATH_NAME,
|
|
|
|
|
CHANGE_OUTPUT_PATH_NAME,
|
|
|
|
|
CHANGE_OUTPUT_STAKING_PATH_NAME,
|
|
|
|
|
POOL_OWNER_STAKING_PATH_NAME,
|
|
|
|
|
SCHEMA_STAKING,
|
|
|
|
|
)
|
|
|
|
|
from ..helpers.utils import (
|
|
|
|
|
derive_public_key,
|
|
|
|
|
get_public_key_hash,
|
|
|
|
|
validate_network_info,
|
|
|
|
|
validate_stake_credential,
|
|
|
|
|
)
|
|
|
|
|
from .. import addresses, certificates, layout, seed
|
|
|
|
|
from ..helpers import INPUT_PREV_HASH_SIZE, LOVELACE_MAX_SUPPLY
|
|
|
|
|
from ..helpers.credential import Credential
|
|
|
|
|
from ..helpers.hash_builder_collection import HashBuilderDict, HashBuilderList
|
|
|
|
|
from ..helpers.paths import SCHEMA_STAKING
|
|
|
|
|
from ..helpers.utils import derive_public_key
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from typing import Any, Awaitable, ClassVar
|
|
|
|
|
from trezor.wire import Context
|
|
|
|
|
from trezor.enums import CardanoAddressType
|
|
|
|
|
from apps.common.paths import PathSchema
|
|
|
|
|
from apps.common import cbor
|
|
|
|
|
|
|
|
|
|
from ..helpers.hash_builder_collection import HashBuilderEmbeddedCBOR
|
|
|
|
|
|
|
|
|
|
CardanoTxResponseType = (
|
|
|
|
|
messages.CardanoTxItemAck | messages.CardanoTxWitnessResponse
|
|
|
|
|
)
|
|
|
|
|
CardanoTxResponseType = CardanoTxItemAck | messages.CardanoTxWitnessResponse
|
|
|
|
|
|
|
|
|
|
_MINTING_POLICY_ID_LENGTH = const(28)
|
|
|
|
|
_MAX_ASSET_NAME_LENGTH = const(32)
|
|
|
|
@ -96,10 +76,12 @@ class Signer:
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
ctx: wire.Context,
|
|
|
|
|
ctx: Context,
|
|
|
|
|
msg: messages.CardanoSignTxInit,
|
|
|
|
|
keychain: seed.Keychain,
|
|
|
|
|
) -> None:
|
|
|
|
|
from ..helpers.account_path_check import AccountPathChecker
|
|
|
|
|
|
|
|
|
|
self.ctx = ctx
|
|
|
|
|
self.msg = msg
|
|
|
|
|
self.keychain = keychain
|
|
|
|
@ -125,12 +107,14 @@ class Signer:
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
self.tx_dict: HashBuilderDict[int, Any] = HashBuilderDict(
|
|
|
|
|
tx_dict_items_count, wire.ProcessError("Invalid tx signing request")
|
|
|
|
|
tx_dict_items_count, ProcessError("Invalid tx signing request")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.should_show_details = False
|
|
|
|
|
|
|
|
|
|
async def sign(self) -> None:
|
|
|
|
|
from trezor.crypto import hashlib
|
|
|
|
|
|
|
|
|
|
hash_fn = hashlib.blake2b(outlen=32)
|
|
|
|
|
self.tx_dict.start(hash_fn)
|
|
|
|
|
with self.tx_dict:
|
|
|
|
@ -150,96 +134,95 @@ class Signer:
|
|
|
|
|
async def _processs_tx_init(self) -> None:
|
|
|
|
|
self._validate_tx_init()
|
|
|
|
|
await self._show_tx_init()
|
|
|
|
|
msg = self.msg # local_cache_attribute
|
|
|
|
|
add = self.tx_dict.add # local_cache_attribute
|
|
|
|
|
HBL = HashBuilderList # local_cache_global
|
|
|
|
|
|
|
|
|
|
inputs_list: HashBuilderList[tuple[bytes, int]] = HashBuilderList(
|
|
|
|
|
self.msg.inputs_count
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_INPUTS, inputs_list):
|
|
|
|
|
inputs_list: HashBuilderList[tuple[bytes, int]] = HBL(msg.inputs_count)
|
|
|
|
|
with add(_TX_BODY_KEY_INPUTS, inputs_list):
|
|
|
|
|
await self._process_inputs(inputs_list)
|
|
|
|
|
|
|
|
|
|
outputs_list: HashBuilderList = HashBuilderList(self.msg.outputs_count)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_OUTPUTS, outputs_list):
|
|
|
|
|
outputs_list: HashBuilderList = HBL(msg.outputs_count)
|
|
|
|
|
with add(_TX_BODY_KEY_OUTPUTS, outputs_list):
|
|
|
|
|
await self._process_outputs(outputs_list)
|
|
|
|
|
|
|
|
|
|
self.tx_dict.add(_TX_BODY_KEY_FEE, self.msg.fee)
|
|
|
|
|
add(_TX_BODY_KEY_FEE, msg.fee)
|
|
|
|
|
|
|
|
|
|
if self.msg.ttl is not None:
|
|
|
|
|
self.tx_dict.add(_TX_BODY_KEY_TTL, self.msg.ttl)
|
|
|
|
|
if msg.ttl is not None:
|
|
|
|
|
add(_TX_BODY_KEY_TTL, msg.ttl)
|
|
|
|
|
|
|
|
|
|
if self.msg.certificates_count > 0:
|
|
|
|
|
certificates_list: HashBuilderList = HashBuilderList(
|
|
|
|
|
self.msg.certificates_count
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_CERTIFICATES, certificates_list):
|
|
|
|
|
if msg.certificates_count > 0:
|
|
|
|
|
certificates_list: HashBuilderList = HBL(msg.certificates_count)
|
|
|
|
|
with add(_TX_BODY_KEY_CERTIFICATES, certificates_list):
|
|
|
|
|
await self._process_certificates(certificates_list)
|
|
|
|
|
|
|
|
|
|
if self.msg.withdrawals_count > 0:
|
|
|
|
|
if msg.withdrawals_count > 0:
|
|
|
|
|
withdrawals_dict: HashBuilderDict[bytes, int] = HashBuilderDict(
|
|
|
|
|
self.msg.withdrawals_count, wire.ProcessError("Invalid withdrawal")
|
|
|
|
|
msg.withdrawals_count, ProcessError("Invalid withdrawal")
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_WITHDRAWALS, withdrawals_dict):
|
|
|
|
|
with add(_TX_BODY_KEY_WITHDRAWALS, withdrawals_dict):
|
|
|
|
|
await self._process_withdrawals(withdrawals_dict)
|
|
|
|
|
|
|
|
|
|
if self.msg.has_auxiliary_data:
|
|
|
|
|
if msg.has_auxiliary_data:
|
|
|
|
|
await self._process_auxiliary_data()
|
|
|
|
|
|
|
|
|
|
if self.msg.validity_interval_start is not None:
|
|
|
|
|
self.tx_dict.add(
|
|
|
|
|
_TX_BODY_KEY_VALIDITY_INTERVAL_START, self.msg.validity_interval_start
|
|
|
|
|
)
|
|
|
|
|
if msg.validity_interval_start is not None:
|
|
|
|
|
add(_TX_BODY_KEY_VALIDITY_INTERVAL_START, msg.validity_interval_start)
|
|
|
|
|
|
|
|
|
|
if self.msg.minting_asset_groups_count > 0:
|
|
|
|
|
if msg.minting_asset_groups_count > 0:
|
|
|
|
|
minting_dict: HashBuilderDict[bytes, HashBuilderDict] = HashBuilderDict(
|
|
|
|
|
self.msg.minting_asset_groups_count,
|
|
|
|
|
wire.ProcessError("Invalid mint token bundle"),
|
|
|
|
|
msg.minting_asset_groups_count,
|
|
|
|
|
ProcessError("Invalid mint token bundle"),
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_MINT, minting_dict):
|
|
|
|
|
with add(_TX_BODY_KEY_MINT, minting_dict):
|
|
|
|
|
await self._process_minting(minting_dict)
|
|
|
|
|
|
|
|
|
|
if self.msg.script_data_hash is not None:
|
|
|
|
|
if msg.script_data_hash is not None:
|
|
|
|
|
await self._process_script_data_hash()
|
|
|
|
|
|
|
|
|
|
if self.msg.collateral_inputs_count > 0:
|
|
|
|
|
collateral_inputs_list: HashBuilderList[
|
|
|
|
|
tuple[bytes, int]
|
|
|
|
|
] = HashBuilderList(self.msg.collateral_inputs_count)
|
|
|
|
|
with self.tx_dict.add(
|
|
|
|
|
_TX_BODY_KEY_COLLATERAL_INPUTS, collateral_inputs_list
|
|
|
|
|
):
|
|
|
|
|
if msg.collateral_inputs_count > 0:
|
|
|
|
|
collateral_inputs_list: HashBuilderList[tuple[bytes, int]] = HBL(
|
|
|
|
|
msg.collateral_inputs_count
|
|
|
|
|
)
|
|
|
|
|
with add(_TX_BODY_KEY_COLLATERAL_INPUTS, collateral_inputs_list):
|
|
|
|
|
await self._process_collateral_inputs(collateral_inputs_list)
|
|
|
|
|
|
|
|
|
|
if self.msg.required_signers_count > 0:
|
|
|
|
|
required_signers_list: HashBuilderList[bytes] = HashBuilderList(
|
|
|
|
|
self.msg.required_signers_count
|
|
|
|
|
if msg.required_signers_count > 0:
|
|
|
|
|
required_signers_list: HashBuilderList[bytes] = HBL(
|
|
|
|
|
msg.required_signers_count
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_REQUIRED_SIGNERS, required_signers_list):
|
|
|
|
|
with add(_TX_BODY_KEY_REQUIRED_SIGNERS, required_signers_list):
|
|
|
|
|
await self._process_required_signers(required_signers_list)
|
|
|
|
|
|
|
|
|
|
if self.msg.include_network_id:
|
|
|
|
|
self.tx_dict.add(_TX_BODY_KEY_NETWORK_ID, self.msg.network_id)
|
|
|
|
|
if msg.include_network_id:
|
|
|
|
|
add(_TX_BODY_KEY_NETWORK_ID, msg.network_id)
|
|
|
|
|
|
|
|
|
|
if self.msg.has_collateral_return:
|
|
|
|
|
if msg.has_collateral_return:
|
|
|
|
|
await self._process_collateral_return()
|
|
|
|
|
|
|
|
|
|
if self.msg.total_collateral is not None:
|
|
|
|
|
self.tx_dict.add(_TX_BODY_KEY_TOTAL_COLLATERAL, self.msg.total_collateral)
|
|
|
|
|
if msg.total_collateral is not None:
|
|
|
|
|
add(_TX_BODY_KEY_TOTAL_COLLATERAL, msg.total_collateral)
|
|
|
|
|
|
|
|
|
|
if self.msg.reference_inputs_count > 0:
|
|
|
|
|
reference_inputs_list: HashBuilderList[tuple[bytes, int]] = HashBuilderList(
|
|
|
|
|
self.msg.reference_inputs_count
|
|
|
|
|
if msg.reference_inputs_count > 0:
|
|
|
|
|
reference_inputs_list: HashBuilderList[tuple[bytes, int]] = HBL(
|
|
|
|
|
msg.reference_inputs_count
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_REFERENCE_INPUTS, reference_inputs_list):
|
|
|
|
|
with add(_TX_BODY_KEY_REFERENCE_INPUTS, reference_inputs_list):
|
|
|
|
|
await self._process_reference_inputs(reference_inputs_list)
|
|
|
|
|
|
|
|
|
|
def _validate_tx_init(self) -> None:
|
|
|
|
|
if self.msg.fee > LOVELACE_MAX_SUPPLY:
|
|
|
|
|
raise wire.ProcessError("Fee is out of range!")
|
|
|
|
|
from ..helpers.utils import validate_network_info
|
|
|
|
|
|
|
|
|
|
msg = self.msg # local_cache_attribute
|
|
|
|
|
|
|
|
|
|
if msg.fee > LOVELACE_MAX_SUPPLY:
|
|
|
|
|
raise ProcessError("Fee is out of range!")
|
|
|
|
|
if (
|
|
|
|
|
self.msg.total_collateral is not None
|
|
|
|
|
and self.msg.total_collateral > LOVELACE_MAX_SUPPLY
|
|
|
|
|
msg.total_collateral is not None
|
|
|
|
|
and msg.total_collateral > LOVELACE_MAX_SUPPLY
|
|
|
|
|
):
|
|
|
|
|
raise wire.ProcessError("Total collateral is out of range!")
|
|
|
|
|
validate_network_info(self.msg.network_id, self.msg.protocol_magic)
|
|
|
|
|
raise ProcessError("Total collateral is out of range!")
|
|
|
|
|
validate_network_info(msg.network_id, msg.protocol_magic)
|
|
|
|
|
|
|
|
|
|
async def _show_tx_init(self) -> None:
|
|
|
|
|
self.should_show_details = await layout.show_tx_init(
|
|
|
|
@ -260,7 +243,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(self.msg.inputs_count):
|
|
|
|
|
input: messages.CardanoTxInput = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxInput
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxInput
|
|
|
|
|
)
|
|
|
|
|
self._validate_input(input)
|
|
|
|
|
await self._show_input(input)
|
|
|
|
@ -268,7 +251,7 @@ class Signer:
|
|
|
|
|
|
|
|
|
|
def _validate_input(self, input: messages.CardanoTxInput) -> None:
|
|
|
|
|
if len(input.prev_hash) != INPUT_PREV_HASH_SIZE:
|
|
|
|
|
raise wire.ProcessError("Invalid input")
|
|
|
|
|
raise ProcessError("Invalid input")
|
|
|
|
|
|
|
|
|
|
async def _show_input(self, input: messages.CardanoTxInput) -> None:
|
|
|
|
|
# We never show the inputs, except for Plutus txs.
|
|
|
|
@ -279,18 +262,18 @@ class Signer:
|
|
|
|
|
async def _process_outputs(self, outputs_list: HashBuilderList) -> None:
|
|
|
|
|
total_amount = 0
|
|
|
|
|
for _ in range(self.msg.outputs_count):
|
|
|
|
|
output: messages.CardanoTxOutput = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxOutput
|
|
|
|
|
output: CardanoTxOutput = await self.ctx.call(
|
|
|
|
|
CardanoTxItemAck(), CardanoTxOutput
|
|
|
|
|
)
|
|
|
|
|
await self._process_output(outputs_list, output)
|
|
|
|
|
|
|
|
|
|
total_amount += output.amount
|
|
|
|
|
|
|
|
|
|
if total_amount > LOVELACE_MAX_SUPPLY:
|
|
|
|
|
raise wire.ProcessError("Total transaction amount is out of range!")
|
|
|
|
|
raise ProcessError("Total transaction amount is out of range!")
|
|
|
|
|
|
|
|
|
|
async def _process_output(
|
|
|
|
|
self, outputs_list: HashBuilderList, output: messages.CardanoTxOutput
|
|
|
|
|
self, outputs_list: HashBuilderList, output: CardanoTxOutput
|
|
|
|
|
) -> None:
|
|
|
|
|
self._validate_output(output)
|
|
|
|
|
should_show = self._should_show_output(output)
|
|
|
|
@ -310,49 +293,53 @@ class Signer:
|
|
|
|
|
await self._process_legacy_output(output_list, output, should_show)
|
|
|
|
|
elif output.format == CardanoTxOutputSerializationFormat.MAP_BABBAGE:
|
|
|
|
|
output_dict: HashBuilderDict[int, Any] = HashBuilderDict(
|
|
|
|
|
output_items_count, wire.ProcessError("Invalid output")
|
|
|
|
|
output_items_count, ProcessError("Invalid output")
|
|
|
|
|
)
|
|
|
|
|
with outputs_list.append(output_dict):
|
|
|
|
|
await self._process_babbage_output(output_dict, output, should_show)
|
|
|
|
|
else:
|
|
|
|
|
raise RuntimeError # should be unreachable
|
|
|
|
|
|
|
|
|
|
def _validate_output(self, output: messages.CardanoTxOutput) -> None:
|
|
|
|
|
if output.address_parameters is not None and output.address is not None:
|
|
|
|
|
raise wire.ProcessError("Invalid output")
|
|
|
|
|
def _validate_output(self, output: CardanoTxOutput) -> None:
|
|
|
|
|
from ..helpers import OUTPUT_DATUM_HASH_SIZE
|
|
|
|
|
|
|
|
|
|
if output.address_parameters is not None:
|
|
|
|
|
addresses.validate_output_address_parameters(output.address_parameters)
|
|
|
|
|
self._fail_if_strict_and_unusual(output.address_parameters)
|
|
|
|
|
address_parameters = output.address_parameters # local_cache_attribute
|
|
|
|
|
|
|
|
|
|
if address_parameters is not None and output.address is not None:
|
|
|
|
|
raise ProcessError("Invalid output")
|
|
|
|
|
|
|
|
|
|
if address_parameters is not None:
|
|
|
|
|
addresses.validate_output_address_parameters(address_parameters)
|
|
|
|
|
self._fail_if_strict_and_unusual(address_parameters)
|
|
|
|
|
elif output.address is not None:
|
|
|
|
|
addresses.validate_output_address(
|
|
|
|
|
output.address, self.msg.protocol_magic, self.msg.network_id
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
raise wire.ProcessError("Invalid output")
|
|
|
|
|
raise ProcessError("Invalid output")
|
|
|
|
|
|
|
|
|
|
# datum hash
|
|
|
|
|
if output.datum_hash is not None:
|
|
|
|
|
if len(output.datum_hash) != OUTPUT_DATUM_HASH_SIZE:
|
|
|
|
|
raise wire.ProcessError("Invalid output datum hash")
|
|
|
|
|
raise ProcessError("Invalid output datum hash")
|
|
|
|
|
|
|
|
|
|
# inline datum
|
|
|
|
|
if output.inline_datum_size > 0:
|
|
|
|
|
if output.format != CardanoTxOutputSerializationFormat.MAP_BABBAGE:
|
|
|
|
|
raise wire.ProcessError("Invalid output")
|
|
|
|
|
raise ProcessError("Invalid output")
|
|
|
|
|
|
|
|
|
|
# datum hash and inline datum are mutually exclusive
|
|
|
|
|
if output.datum_hash is not None and output.inline_datum_size > 0:
|
|
|
|
|
raise wire.ProcessError("Invalid output")
|
|
|
|
|
raise ProcessError("Invalid output")
|
|
|
|
|
|
|
|
|
|
# reference script
|
|
|
|
|
if output.reference_script_size > 0:
|
|
|
|
|
if output.format != CardanoTxOutputSerializationFormat.MAP_BABBAGE:
|
|
|
|
|
raise wire.ProcessError("Invalid output")
|
|
|
|
|
raise ProcessError("Invalid output")
|
|
|
|
|
|
|
|
|
|
self.account_path_checker.add_output(output)
|
|
|
|
|
|
|
|
|
|
async def _show_output_init(self, output: messages.CardanoTxOutput) -> None:
|
|
|
|
|
async def _show_output_init(self, output: CardanoTxOutput) -> None:
|
|
|
|
|
address_type = self._get_output_address_type(output)
|
|
|
|
|
if (
|
|
|
|
|
output.datum_hash is None
|
|
|
|
@ -393,7 +380,7 @@ class Signer:
|
|
|
|
|
Credential.stake_credential(address_parameters),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _should_show_output(self, output: messages.CardanoTxOutput) -> bool:
|
|
|
|
|
def _should_show_output(self, output: CardanoTxOutput) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Determines whether the output should be shown. Extracted from _show_output
|
|
|
|
|
because of readability.
|
|
|
|
@ -419,12 +406,14 @@ class Signer:
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _is_change_output(self, output: messages.CardanoTxOutput) -> bool:
|
|
|
|
|
def _is_change_output(self, output: CardanoTxOutput) -> bool:
|
|
|
|
|
"""Used only to determine what message to show to the user when confirming sending."""
|
|
|
|
|
return output.address_parameters is not None
|
|
|
|
|
|
|
|
|
|
def _is_simple_change_output(self, output: messages.CardanoTxOutput) -> bool:
|
|
|
|
|
def _is_simple_change_output(self, output: CardanoTxOutput) -> bool:
|
|
|
|
|
"""Used to determine whether an output is a change output with ordinary credentials."""
|
|
|
|
|
from ..helpers.credential import should_show_credentials
|
|
|
|
|
|
|
|
|
|
return output.address_parameters is not None and not should_show_credentials(
|
|
|
|
|
output.address_parameters
|
|
|
|
|
)
|
|
|
|
@ -432,7 +421,7 @@ class Signer:
|
|
|
|
|
async def _process_legacy_output(
|
|
|
|
|
self,
|
|
|
|
|
output_list: HashBuilderList,
|
|
|
|
|
output: messages.CardanoTxOutput,
|
|
|
|
|
output: CardanoTxOutput,
|
|
|
|
|
should_show: bool,
|
|
|
|
|
) -> None:
|
|
|
|
|
address = self._get_output_address(output)
|
|
|
|
@ -457,23 +446,27 @@ class Signer:
|
|
|
|
|
async def _process_babbage_output(
|
|
|
|
|
self,
|
|
|
|
|
output_dict: HashBuilderDict[int, Any],
|
|
|
|
|
output: messages.CardanoTxOutput,
|
|
|
|
|
output: CardanoTxOutput,
|
|
|
|
|
should_show: bool,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""
|
|
|
|
|
This output format corresponds to the post-Alonzo format in CDDL.
|
|
|
|
|
Note that it is to be used also for outputs with no Plutus elements.
|
|
|
|
|
"""
|
|
|
|
|
from ..helpers.hash_builder_collection import HashBuilderEmbeddedCBOR
|
|
|
|
|
|
|
|
|
|
add = output_dict.add # local_cache_attribute
|
|
|
|
|
|
|
|
|
|
address = self._get_output_address(output)
|
|
|
|
|
output_dict.add(_BABBAGE_OUTPUT_KEY_ADDRESS, address)
|
|
|
|
|
add(_BABBAGE_OUTPUT_KEY_ADDRESS, address)
|
|
|
|
|
|
|
|
|
|
if output.asset_groups_count == 0:
|
|
|
|
|
# Only amount is added to the dict.
|
|
|
|
|
output_dict.add(_BABBAGE_OUTPUT_KEY_AMOUNT, output.amount)
|
|
|
|
|
add(_BABBAGE_OUTPUT_KEY_AMOUNT, output.amount)
|
|
|
|
|
else:
|
|
|
|
|
# [amount, asset_groups] is added to the dict.
|
|
|
|
|
output_value_list: HashBuilderList = HashBuilderList(2)
|
|
|
|
|
with output_dict.add(_BABBAGE_OUTPUT_KEY_AMOUNT, output_value_list):
|
|
|
|
|
with add(_BABBAGE_OUTPUT_KEY_AMOUNT, output_value_list):
|
|
|
|
|
await self._process_output_value(output_value_list, output, should_show)
|
|
|
|
|
|
|
|
|
|
if output.datum_hash is not None:
|
|
|
|
@ -481,13 +474,13 @@ class Signer:
|
|
|
|
|
await self._show_if_showing_details(
|
|
|
|
|
layout.confirm_datum_hash(self.ctx, output.datum_hash)
|
|
|
|
|
)
|
|
|
|
|
output_dict.add(
|
|
|
|
|
add(
|
|
|
|
|
_BABBAGE_OUTPUT_KEY_DATUM_OPTION,
|
|
|
|
|
(_DATUM_OPTION_KEY_HASH, output.datum_hash),
|
|
|
|
|
)
|
|
|
|
|
elif output.inline_datum_size > 0:
|
|
|
|
|
inline_datum_list: HashBuilderList = HashBuilderList(2)
|
|
|
|
|
with output_dict.add(_BABBAGE_OUTPUT_KEY_DATUM_OPTION, inline_datum_list):
|
|
|
|
|
with add(_BABBAGE_OUTPUT_KEY_DATUM_OPTION, inline_datum_list):
|
|
|
|
|
inline_datum_list.append(_DATUM_OPTION_KEY_INLINE)
|
|
|
|
|
inline_datum_cbor: HashBuilderEmbeddedCBOR = HashBuilderEmbeddedCBOR(
|
|
|
|
|
output.inline_datum_size
|
|
|
|
@ -501,9 +494,7 @@ class Signer:
|
|
|
|
|
reference_script_cbor: HashBuilderEmbeddedCBOR = HashBuilderEmbeddedCBOR(
|
|
|
|
|
output.reference_script_size
|
|
|
|
|
)
|
|
|
|
|
with output_dict.add(
|
|
|
|
|
_BABBAGE_OUTPUT_KEY_REFERENCE_SCRIPT, reference_script_cbor
|
|
|
|
|
):
|
|
|
|
|
with add(_BABBAGE_OUTPUT_KEY_REFERENCE_SCRIPT, reference_script_cbor):
|
|
|
|
|
await self._process_reference_script(
|
|
|
|
|
reference_script_cbor, output.reference_script_size, should_show
|
|
|
|
|
)
|
|
|
|
@ -511,7 +502,7 @@ class Signer:
|
|
|
|
|
async def _process_output_value(
|
|
|
|
|
self,
|
|
|
|
|
output_value_list: HashBuilderList,
|
|
|
|
|
output: messages.CardanoTxOutput,
|
|
|
|
|
output: CardanoTxOutput,
|
|
|
|
|
should_show_tokens: bool,
|
|
|
|
|
) -> None:
|
|
|
|
|
"""Should be used only when the output contains tokens."""
|
|
|
|
@ -523,7 +514,7 @@ class Signer:
|
|
|
|
|
bytes, HashBuilderDict[bytes, int]
|
|
|
|
|
] = HashBuilderDict(
|
|
|
|
|
output.asset_groups_count,
|
|
|
|
|
wire.ProcessError("Invalid token bundle in output"),
|
|
|
|
|
ProcessError("Invalid token bundle in output"),
|
|
|
|
|
)
|
|
|
|
|
with output_value_list.append(asset_groups_dict):
|
|
|
|
|
await self._process_asset_groups(
|
|
|
|
@ -542,13 +533,13 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(asset_groups_count):
|
|
|
|
|
asset_group: messages.CardanoAssetGroup = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoAssetGroup
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoAssetGroup
|
|
|
|
|
)
|
|
|
|
|
self._validate_asset_group(asset_group)
|
|
|
|
|
|
|
|
|
|
tokens: HashBuilderDict[bytes, int] = HashBuilderDict(
|
|
|
|
|
asset_group.tokens_count,
|
|
|
|
|
wire.ProcessError("Invalid token bundle in output"),
|
|
|
|
|
ProcessError("Invalid token bundle in output"),
|
|
|
|
|
)
|
|
|
|
|
with asset_groups_dict.add(asset_group.policy_id, tokens):
|
|
|
|
|
await self._process_tokens(
|
|
|
|
@ -562,9 +553,9 @@ class Signer:
|
|
|
|
|
self, asset_group: messages.CardanoAssetGroup, is_mint: bool = False
|
|
|
|
|
) -> None:
|
|
|
|
|
INVALID_TOKEN_BUNDLE = (
|
|
|
|
|
wire.ProcessError("Invalid mint token bundle")
|
|
|
|
|
ProcessError("Invalid mint token bundle")
|
|
|
|
|
if is_mint
|
|
|
|
|
else wire.ProcessError("Invalid token bundle in output")
|
|
|
|
|
else ProcessError("Invalid token bundle in output")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if len(asset_group.policy_id) != _MINTING_POLICY_ID_LENGTH:
|
|
|
|
@ -583,7 +574,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(tokens_count):
|
|
|
|
|
token: messages.CardanoToken = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoToken
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoToken
|
|
|
|
|
)
|
|
|
|
|
self._validate_token(token)
|
|
|
|
|
if should_show_tokens:
|
|
|
|
@ -596,9 +587,9 @@ class Signer:
|
|
|
|
|
self, token: messages.CardanoToken, is_mint: bool = False
|
|
|
|
|
) -> None:
|
|
|
|
|
INVALID_TOKEN_BUNDLE = (
|
|
|
|
|
wire.ProcessError("Invalid mint token bundle")
|
|
|
|
|
ProcessError("Invalid mint token bundle")
|
|
|
|
|
if is_mint
|
|
|
|
|
else wire.ProcessError("Invalid token bundle in output")
|
|
|
|
|
else ProcessError("Invalid token bundle in output")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if is_mint:
|
|
|
|
@ -624,13 +615,13 @@ class Signer:
|
|
|
|
|
chunks_count = self._get_chunks_count(inline_datum_size)
|
|
|
|
|
for chunk_number in range(chunks_count):
|
|
|
|
|
chunk: messages.CardanoTxInlineDatumChunk = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxInlineDatumChunk
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxInlineDatumChunk
|
|
|
|
|
)
|
|
|
|
|
self._validate_chunk(
|
|
|
|
|
chunk.data,
|
|
|
|
|
chunk_number,
|
|
|
|
|
chunks_count,
|
|
|
|
|
wire.ProcessError("Invalid inline datum chunk"),
|
|
|
|
|
ProcessError("Invalid inline datum chunk"),
|
|
|
|
|
)
|
|
|
|
|
if chunk_number == 0 and should_show:
|
|
|
|
|
await self._show_if_showing_details(
|
|
|
|
@ -651,13 +642,13 @@ class Signer:
|
|
|
|
|
chunks_count = self._get_chunks_count(reference_script_size)
|
|
|
|
|
for chunk_number in range(chunks_count):
|
|
|
|
|
chunk: messages.CardanoTxReferenceScriptChunk = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxReferenceScriptChunk
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxReferenceScriptChunk
|
|
|
|
|
)
|
|
|
|
|
self._validate_chunk(
|
|
|
|
|
chunk.data,
|
|
|
|
|
chunk_number,
|
|
|
|
|
chunks_count,
|
|
|
|
|
wire.ProcessError("Invalid reference script chunk"),
|
|
|
|
|
ProcessError("Invalid reference script chunk"),
|
|
|
|
|
)
|
|
|
|
|
if chunk_number == 0 and should_show:
|
|
|
|
|
await self._show_if_showing_details(
|
|
|
|
@ -672,7 +663,7 @@ class Signer:
|
|
|
|
|
async def _process_certificates(self, certificates_list: HashBuilderList) -> None:
|
|
|
|
|
for _ in range(self.msg.certificates_count):
|
|
|
|
|
certificate: messages.CardanoTxCertificate = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxCertificate
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxCertificate
|
|
|
|
|
)
|
|
|
|
|
self._validate_certificate(certificate)
|
|
|
|
|
await self._show_certificate(certificate)
|
|
|
|
@ -725,6 +716,8 @@ class Signer:
|
|
|
|
|
async def _show_certificate(
|
|
|
|
|
self, certificate: messages.CardanoTxCertificate
|
|
|
|
|
) -> None:
|
|
|
|
|
from ..helpers.paths import CERTIFICATE_PATH_NAME
|
|
|
|
|
|
|
|
|
|
if certificate.path:
|
|
|
|
|
await self._fail_or_warn_if_invalid_path(
|
|
|
|
|
SCHEMA_STAKING, certificate.path, CERTIFICATE_PATH_NAME
|
|
|
|
@ -749,7 +742,7 @@ class Signer:
|
|
|
|
|
owners_as_path_count = 0
|
|
|
|
|
for _ in range(owners_count):
|
|
|
|
|
owner: messages.CardanoPoolOwner = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoPoolOwner
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoPoolOwner
|
|
|
|
|
)
|
|
|
|
|
certificates.validate_pool_owner(owner, self.account_path_checker)
|
|
|
|
|
await self._show_pool_owner(owner)
|
|
|
|
@ -763,6 +756,8 @@ class Signer:
|
|
|
|
|
certificates.assert_cond(owners_as_path_count == 1)
|
|
|
|
|
|
|
|
|
|
async def _show_pool_owner(self, owner: messages.CardanoPoolOwner) -> None:
|
|
|
|
|
from ..helpers.paths import POOL_OWNER_STAKING_PATH_NAME
|
|
|
|
|
|
|
|
|
|
if owner.staking_key_path:
|
|
|
|
|
await self._fail_or_warn_if_invalid_path(
|
|
|
|
|
SCHEMA_STAKING, owner.staking_key_path, POOL_OWNER_STAKING_PATH_NAME
|
|
|
|
@ -781,7 +776,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(relays_count):
|
|
|
|
|
relay: messages.CardanoPoolRelayParameters = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoPoolRelayParameters
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoPoolRelayParameters
|
|
|
|
|
)
|
|
|
|
|
certificates.validate_pool_relay(relay)
|
|
|
|
|
relays_list.append(certificates.cborize_pool_relay(relay))
|
|
|
|
@ -793,7 +788,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(self.msg.withdrawals_count):
|
|
|
|
|
withdrawal: messages.CardanoTxWithdrawal = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxWithdrawal
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxWithdrawal
|
|
|
|
|
)
|
|
|
|
|
self._validate_withdrawal(withdrawal)
|
|
|
|
|
address_bytes = self._derive_withdrawal_address_bytes(withdrawal)
|
|
|
|
@ -805,23 +800,29 @@ class Signer:
|
|
|
|
|
withdrawals_dict.add(address_bytes, withdrawal.amount)
|
|
|
|
|
|
|
|
|
|
def _validate_withdrawal(self, withdrawal: messages.CardanoTxWithdrawal) -> None:
|
|
|
|
|
from ..helpers.utils import validate_stake_credential
|
|
|
|
|
|
|
|
|
|
validate_stake_credential(
|
|
|
|
|
withdrawal.path,
|
|
|
|
|
withdrawal.script_hash,
|
|
|
|
|
withdrawal.key_hash,
|
|
|
|
|
wire.ProcessError("Invalid withdrawal"),
|
|
|
|
|
ProcessError("Invalid withdrawal"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not 0 <= withdrawal.amount < LOVELACE_MAX_SUPPLY:
|
|
|
|
|
raise wire.ProcessError("Invalid withdrawal")
|
|
|
|
|
raise ProcessError("Invalid withdrawal")
|
|
|
|
|
|
|
|
|
|
self.account_path_checker.add_withdrawal(withdrawal)
|
|
|
|
|
|
|
|
|
|
# auxiliary data
|
|
|
|
|
|
|
|
|
|
async def _process_auxiliary_data(self) -> None:
|
|
|
|
|
from .. import auxiliary_data
|
|
|
|
|
|
|
|
|
|
msg = self.msg # local_cache_attribute
|
|
|
|
|
|
|
|
|
|
data: messages.CardanoTxAuxiliaryData = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxAuxiliaryData
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxAuxiliaryData
|
|
|
|
|
)
|
|
|
|
|
auxiliary_data.validate(data)
|
|
|
|
|
|
|
|
|
@ -829,15 +830,15 @@ class Signer:
|
|
|
|
|
auxiliary_data_hash,
|
|
|
|
|
auxiliary_data_supplement,
|
|
|
|
|
) = auxiliary_data.get_hash_and_supplement(
|
|
|
|
|
self.keychain, data, self.msg.protocol_magic, self.msg.network_id
|
|
|
|
|
self.keychain, data, msg.protocol_magic, msg.network_id
|
|
|
|
|
)
|
|
|
|
|
await auxiliary_data.show(
|
|
|
|
|
self.ctx,
|
|
|
|
|
self.keychain,
|
|
|
|
|
auxiliary_data_hash,
|
|
|
|
|
data.governance_registration_parameters,
|
|
|
|
|
self.msg.protocol_magic,
|
|
|
|
|
self.msg.network_id,
|
|
|
|
|
msg.protocol_magic,
|
|
|
|
|
msg.network_id,
|
|
|
|
|
self.should_show_details,
|
|
|
|
|
)
|
|
|
|
|
self.tx_dict.add(_TX_BODY_KEY_AUXILIARY_DATA, auxiliary_data_hash)
|
|
|
|
@ -850,19 +851,19 @@ class Signer:
|
|
|
|
|
self, minting_dict: HashBuilderDict[bytes, HashBuilderDict]
|
|
|
|
|
) -> None:
|
|
|
|
|
token_minting: messages.CardanoTxMint = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxMint
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxMint
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await layout.warn_tx_contains_mint(self.ctx)
|
|
|
|
|
|
|
|
|
|
for _ in range(token_minting.asset_groups_count):
|
|
|
|
|
asset_group: messages.CardanoAssetGroup = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoAssetGroup
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoAssetGroup
|
|
|
|
|
)
|
|
|
|
|
self._validate_asset_group(asset_group, is_mint=True)
|
|
|
|
|
|
|
|
|
|
tokens: HashBuilderDict[bytes, int] = HashBuilderDict(
|
|
|
|
|
asset_group.tokens_count, wire.ProcessError("Invalid mint token bundle")
|
|
|
|
|
asset_group.tokens_count, ProcessError("Invalid mint token bundle")
|
|
|
|
|
)
|
|
|
|
|
with minting_dict.add(asset_group.policy_id, tokens):
|
|
|
|
|
await self._process_minting_tokens(
|
|
|
|
@ -881,7 +882,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(tokens_count):
|
|
|
|
|
token: messages.CardanoToken = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoToken
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoToken
|
|
|
|
|
)
|
|
|
|
|
self._validate_token(token, is_mint=True)
|
|
|
|
|
await layout.confirm_token_minting(self.ctx, policy_id, token)
|
|
|
|
@ -900,9 +901,11 @@ class Signer:
|
|
|
|
|
self.tx_dict.add(_TX_BODY_KEY_SCRIPT_DATA_HASH, self.msg.script_data_hash)
|
|
|
|
|
|
|
|
|
|
def _validate_script_data_hash(self) -> None:
|
|
|
|
|
from ..helpers import SCRIPT_DATA_HASH_SIZE
|
|
|
|
|
|
|
|
|
|
assert self.msg.script_data_hash is not None
|
|
|
|
|
if len(self.msg.script_data_hash) != SCRIPT_DATA_HASH_SIZE:
|
|
|
|
|
raise wire.ProcessError("Invalid script data hash")
|
|
|
|
|
raise ProcessError("Invalid script data hash")
|
|
|
|
|
|
|
|
|
|
# collateral inputs
|
|
|
|
|
|
|
|
|
@ -911,7 +914,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(self.msg.collateral_inputs_count):
|
|
|
|
|
collateral_input: messages.CardanoTxCollateralInput = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxCollateralInput
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxCollateralInput
|
|
|
|
|
)
|
|
|
|
|
self._validate_collateral_input(collateral_input)
|
|
|
|
|
await self._show_collateral_input(collateral_input)
|
|
|
|
@ -923,7 +926,7 @@ class Signer:
|
|
|
|
|
self, collateral_input: messages.CardanoTxCollateralInput
|
|
|
|
|
) -> None:
|
|
|
|
|
if len(collateral_input.prev_hash) != INPUT_PREV_HASH_SIZE:
|
|
|
|
|
raise wire.ProcessError("Invalid collateral input")
|
|
|
|
|
raise ProcessError("Invalid collateral input")
|
|
|
|
|
|
|
|
|
|
async def _show_collateral_input(
|
|
|
|
|
self, collateral_input: messages.CardanoTxCollateralInput
|
|
|
|
@ -938,9 +941,11 @@ class Signer:
|
|
|
|
|
async def _process_required_signers(
|
|
|
|
|
self, required_signers_list: HashBuilderList[bytes]
|
|
|
|
|
) -> None:
|
|
|
|
|
from ..helpers.utils import get_public_key_hash
|
|
|
|
|
|
|
|
|
|
for _ in range(self.msg.required_signers_count):
|
|
|
|
|
required_signer: messages.CardanoTxRequiredSigner = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxRequiredSigner
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxRequiredSigner
|
|
|
|
|
)
|
|
|
|
|
self._validate_required_signer(required_signer)
|
|
|
|
|
await self._show_if_showing_details(
|
|
|
|
@ -955,19 +960,23 @@ class Signer:
|
|
|
|
|
def _validate_required_signer(
|
|
|
|
|
self, required_signer: messages.CardanoTxRequiredSigner
|
|
|
|
|
) -> None:
|
|
|
|
|
INVALID_REQUIRED_SIGNER = wire.ProcessError("Invalid required signer")
|
|
|
|
|
from ..helpers import ADDRESS_KEY_HASH_SIZE
|
|
|
|
|
|
|
|
|
|
key_path = required_signer.key_path # local_cache_attribute
|
|
|
|
|
|
|
|
|
|
if required_signer.key_hash and required_signer.key_path:
|
|
|
|
|
INVALID_REQUIRED_SIGNER = ProcessError("Invalid required signer")
|
|
|
|
|
|
|
|
|
|
if required_signer.key_hash and key_path:
|
|
|
|
|
raise INVALID_REQUIRED_SIGNER
|
|
|
|
|
|
|
|
|
|
if required_signer.key_hash:
|
|
|
|
|
if len(required_signer.key_hash) != ADDRESS_KEY_HASH_SIZE:
|
|
|
|
|
raise INVALID_REQUIRED_SIGNER
|
|
|
|
|
elif required_signer.key_path:
|
|
|
|
|
elif key_path:
|
|
|
|
|
if not (
|
|
|
|
|
seed.is_shelley_path(required_signer.key_path)
|
|
|
|
|
or seed.is_multisig_path(required_signer.key_path)
|
|
|
|
|
or seed.is_minting_path(required_signer.key_path)
|
|
|
|
|
seed.is_shelley_path(key_path)
|
|
|
|
|
or seed.is_multisig_path(key_path)
|
|
|
|
|
or seed.is_minting_path(key_path)
|
|
|
|
|
):
|
|
|
|
|
raise INVALID_REQUIRED_SIGNER
|
|
|
|
|
else:
|
|
|
|
@ -976,8 +985,8 @@ class Signer:
|
|
|
|
|
# collateral return
|
|
|
|
|
|
|
|
|
|
async def _process_collateral_return(self) -> None:
|
|
|
|
|
output: messages.CardanoTxOutput = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxOutput
|
|
|
|
|
output: CardanoTxOutput = await self.ctx.call(
|
|
|
|
|
CardanoTxItemAck(), CardanoTxOutput
|
|
|
|
|
)
|
|
|
|
|
self._validate_collateral_return(output)
|
|
|
|
|
should_show_init = self._should_show_collateral_return_init(output)
|
|
|
|
@ -995,7 +1004,7 @@ class Signer:
|
|
|
|
|
)
|
|
|
|
|
elif output.format == CardanoTxOutputSerializationFormat.MAP_BABBAGE:
|
|
|
|
|
output_dict: HashBuilderDict[int, Any] = HashBuilderDict(
|
|
|
|
|
output_items_count, wire.ProcessError("Invalid collateral return")
|
|
|
|
|
output_items_count, ProcessError("Invalid collateral return")
|
|
|
|
|
)
|
|
|
|
|
with self.tx_dict.add(_TX_BODY_KEY_COLLATERAL_RETURN, output_dict):
|
|
|
|
|
await self._process_babbage_output(
|
|
|
|
@ -1004,23 +1013,21 @@ class Signer:
|
|
|
|
|
else:
|
|
|
|
|
raise RuntimeError # should be unreachable
|
|
|
|
|
|
|
|
|
|
def _validate_collateral_return(self, output: messages.CardanoTxOutput) -> None:
|
|
|
|
|
def _validate_collateral_return(self, output: CardanoTxOutput) -> None:
|
|
|
|
|
self._validate_output(output)
|
|
|
|
|
|
|
|
|
|
address_type = self._get_output_address_type(output)
|
|
|
|
|
if address_type not in addresses.ADDRESS_TYPES_PAYMENT_KEY:
|
|
|
|
|
raise wire.ProcessError("Invalid collateral return")
|
|
|
|
|
raise ProcessError("Invalid collateral return")
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
output.datum_hash is not None
|
|
|
|
|
or output.inline_datum_size > 0
|
|
|
|
|
or output.reference_script_size > 0
|
|
|
|
|
):
|
|
|
|
|
raise wire.ProcessError("Invalid collateral return")
|
|
|
|
|
raise ProcessError("Invalid collateral return")
|
|
|
|
|
|
|
|
|
|
async def _show_collateral_return_init(
|
|
|
|
|
self, output: messages.CardanoTxOutput
|
|
|
|
|
) -> None:
|
|
|
|
|
async def _show_collateral_return_init(self, output: CardanoTxOutput) -> None:
|
|
|
|
|
# We don't display missing datum warning since datums are forbidden.
|
|
|
|
|
|
|
|
|
|
if output.asset_groups_count > 0:
|
|
|
|
@ -1050,9 +1057,7 @@ class Signer:
|
|
|
|
|
self.msg.network_id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _should_show_collateral_return_init(
|
|
|
|
|
self, output: messages.CardanoTxOutput
|
|
|
|
|
) -> bool:
|
|
|
|
|
def _should_show_collateral_return_init(self, output: CardanoTxOutput) -> bool:
|
|
|
|
|
if self.msg.total_collateral is None:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
@ -1061,9 +1066,7 @@ class Signer:
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _should_show_collateral_return_tokens(
|
|
|
|
|
self, output: messages.CardanoTxOutput
|
|
|
|
|
) -> bool:
|
|
|
|
|
def _should_show_collateral_return_tokens(self, output: CardanoTxOutput) -> bool:
|
|
|
|
|
if self._is_simple_change_output(output):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
@ -1076,7 +1079,7 @@ class Signer:
|
|
|
|
|
) -> None:
|
|
|
|
|
for _ in range(self.msg.reference_inputs_count):
|
|
|
|
|
reference_input: messages.CardanoTxReferenceInput = await self.ctx.call(
|
|
|
|
|
messages.CardanoTxItemAck(), messages.CardanoTxReferenceInput
|
|
|
|
|
CardanoTxItemAck(), messages.CardanoTxReferenceInput
|
|
|
|
|
)
|
|
|
|
|
self._validate_reference_input(reference_input)
|
|
|
|
|
await self._show_if_showing_details(
|
|
|
|
@ -1090,12 +1093,12 @@ class Signer:
|
|
|
|
|
self, reference_input: messages.CardanoTxReferenceInput
|
|
|
|
|
) -> None:
|
|
|
|
|
if len(reference_input.prev_hash) != INPUT_PREV_HASH_SIZE:
|
|
|
|
|
raise wire.ProcessError("Invalid reference input")
|
|
|
|
|
raise ProcessError("Invalid reference input")
|
|
|
|
|
|
|
|
|
|
# witness requests
|
|
|
|
|
|
|
|
|
|
async def _process_witness_requests(self, tx_hash: bytes) -> CardanoTxResponseType:
|
|
|
|
|
response: CardanoTxResponseType = messages.CardanoTxItemAck()
|
|
|
|
|
response: CardanoTxResponseType = CardanoTxItemAck()
|
|
|
|
|
|
|
|
|
|
for _ in range(self.msg.witness_requests_count):
|
|
|
|
|
witness_request = await self.ctx.call(
|
|
|
|
@ -1126,7 +1129,7 @@ class Signer:
|
|
|
|
|
|
|
|
|
|
def _assert_tx_init_cond(self, condition: bool) -> None:
|
|
|
|
|
if not condition:
|
|
|
|
|
raise wire.ProcessError("Invalid tx signing request")
|
|
|
|
|
raise ProcessError("Invalid tx signing request")
|
|
|
|
|
|
|
|
|
|
def _is_network_id_verifiable(self) -> bool:
|
|
|
|
|
"""
|
|
|
|
@ -1144,7 +1147,7 @@ class Signer:
|
|
|
|
|
or self.msg.withdrawals_count != 0
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _get_output_address(self, output: messages.CardanoTxOutput) -> bytes:
|
|
|
|
|
def _get_output_address(self, output: CardanoTxOutput) -> bytes:
|
|
|
|
|
if output.address_parameters:
|
|
|
|
|
return addresses.derive_bytes(
|
|
|
|
|
self.keychain,
|
|
|
|
@ -1156,9 +1159,7 @@ class Signer:
|
|
|
|
|
assert output.address is not None # _validate_output
|
|
|
|
|
return addresses.get_bytes_unsafe(output.address)
|
|
|
|
|
|
|
|
|
|
def _get_output_address_type(
|
|
|
|
|
self, output: messages.CardanoTxOutput
|
|
|
|
|
) -> CardanoAddressType:
|
|
|
|
|
def _get_output_address_type(self, output: CardanoTxOutput) -> CardanoAddressType:
|
|
|
|
|
if output.address_parameters:
|
|
|
|
|
return output.address_parameters.address_type
|
|
|
|
|
assert output.address is not None # _validate_output
|
|
|
|
@ -1167,6 +1168,8 @@ class Signer:
|
|
|
|
|
def _derive_withdrawal_address_bytes(
|
|
|
|
|
self, withdrawal: messages.CardanoTxWithdrawal
|
|
|
|
|
) -> bytes:
|
|
|
|
|
from trezor.enums import CardanoAddressType
|
|
|
|
|
|
|
|
|
|
reward_address_type = (
|
|
|
|
|
CardanoAddressType.REWARD
|
|
|
|
|
if withdrawal.path or withdrawal.key_hash
|
|
|
|
@ -1193,7 +1196,7 @@ class Signer:
|
|
|
|
|
chunk_data: bytes,
|
|
|
|
|
chunk_number: int,
|
|
|
|
|
chunks_count: int,
|
|
|
|
|
error: wire.ProcessError,
|
|
|
|
|
error: ProcessError,
|
|
|
|
|
) -> None:
|
|
|
|
|
if chunk_number < chunks_count - 1 and len(chunk_data) != _MAX_CHUNK_SIZE:
|
|
|
|
|
raise error
|
|
|
|
@ -1221,6 +1224,8 @@ class Signer:
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _sign_tx_hash(self, tx_body_hash: bytes, path: list[int]) -> bytes:
|
|
|
|
|
from trezor.crypto.curve import ed25519
|
|
|
|
|
|
|
|
|
|
node = self.keychain.derive(path)
|
|
|
|
|
return ed25519.sign_ext(
|
|
|
|
|
node.private_key(), node.private_key_ext(), tx_body_hash
|
|
|
|
@ -1234,21 +1239,26 @@ class Signer:
|
|
|
|
|
|
|
|
|
|
async def _fail_or_warn_path(self, path: list[int], path_name: str) -> None:
|
|
|
|
|
if safety_checks.is_strict():
|
|
|
|
|
raise wire.DataError(f"Invalid {path_name.lower()}")
|
|
|
|
|
raise DataError(f"Invalid {path_name.lower()}")
|
|
|
|
|
else:
|
|
|
|
|
await layout.warn_path(self.ctx, path, path_name)
|
|
|
|
|
|
|
|
|
|
def _fail_if_strict_and_unusual(
|
|
|
|
|
self, address_parameters: messages.CardanoAddressParametersType
|
|
|
|
|
) -> None:
|
|
|
|
|
from ..helpers.paths import (
|
|
|
|
|
CHANGE_OUTPUT_PATH_NAME,
|
|
|
|
|
CHANGE_OUTPUT_STAKING_PATH_NAME,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not safety_checks.is_strict():
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if Credential.payment_credential(address_parameters).is_unusual_path:
|
|
|
|
|
raise wire.DataError(f"Invalid {CHANGE_OUTPUT_PATH_NAME.lower()}")
|
|
|
|
|
raise DataError(f"Invalid {CHANGE_OUTPUT_PATH_NAME.lower()}")
|
|
|
|
|
|
|
|
|
|
if Credential.stake_credential(address_parameters).is_unusual_path:
|
|
|
|
|
raise wire.DataError(f"Invalid {CHANGE_OUTPUT_STAKING_PATH_NAME.lower()}")
|
|
|
|
|
raise 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:
|
|
|
|
|