1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-18 12:28:09 +00:00

feat(cardano): allow external reward addresses in governance registrations

This commit is contained in:
David Misiak 2022-12-06 20:05:27 +01:00 committed by matejcik
parent e2416bcec2
commit a94cfa1a13
7 changed files with 81 additions and 28 deletions

View File

@ -369,13 +369,14 @@ message CardanoGovernanceRegistrationDelegation {
* @embed * @embed
*/ */
message CardanoGovernanceRegistrationParametersType { message CardanoGovernanceRegistrationParametersType {
optional bytes voting_public_key = 1; optional bytes voting_public_key = 1; // mutually exclusive with delegations
repeated uint32 staking_path = 2; repeated uint32 staking_path = 2;
required CardanoAddressParametersType reward_address_parameters = 3; optional CardanoAddressParametersType reward_address_parameters = 3; // mutually exclusive with reward_address
required uint64 nonce = 4; required uint64 nonce = 4;
optional CardanoGovernanceRegistrationFormat format = 5 [default=CIP15]; optional CardanoGovernanceRegistrationFormat format = 5 [default=CIP15];
repeated CardanoGovernanceRegistrationDelegation delegations = 6; // mutually exclusive with voting_public_key; max 32 delegations repeated CardanoGovernanceRegistrationDelegation delegations = 6; // mutually exclusive with voting_public_key; max 32 delegations
optional uint64 voting_purpose = 7; optional uint64 voting_purpose = 7;
optional string reward_address = 8; // mutually exclusive with reward_address_parameters
} }
/** /**

View File

@ -231,6 +231,16 @@ def validate_output_address_parameters(
assert_params_cond(parameters.address_type in ADDRESS_TYPES_PAYMENT_KEY) assert_params_cond(parameters.address_type in ADDRESS_TYPES_PAYMENT_KEY)
def validate_governance_reward_address_parameters(
parameters: messages.CardanoAddressParametersType,
) -> None:
validate_address_parameters(parameters)
# Despite the name, the address doesn't have to be a REWARD address.
# see also validate_governance_reward_address
assert_params_cond(parameters.address_type in ADDRESS_TYPES_SHELLEY)
def assert_cond(condition: bool) -> None: def assert_cond(condition: bool) -> None:
if not condition: if not condition:
raise ProcessError("Invalid address") raise ProcessError("Invalid address")
@ -287,6 +297,13 @@ def validate_reward_address(address: str, protocol_magic: int, network_id: int)
) )
def validate_governance_reward_address(
address: str, protocol_magic: int, network_id: int
) -> None:
address_type = _validate_and_get_type(address, protocol_magic, network_id)
assert_cond(address_type in ADDRESS_TYPES_SHELLEY)
def get_bytes_unsafe(address: str) -> bytes: def get_bytes_unsafe(address: str) -> bytes:
try: try:
address_bytes = bech32.decode_unsafe(address) address_bytes = bech32.decode_unsafe(address)

View File

@ -2,7 +2,7 @@ from micropython import const
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor.crypto import hashlib from trezor.crypto import hashlib
from trezor.enums import CardanoAddressType, CardanoGovernanceRegistrationFormat from trezor.enums import CardanoGovernanceRegistrationFormat
from apps.common import cbor from apps.common import cbor
@ -38,7 +38,11 @@ def assert_cond(condition: bool) -> None:
raise wire.ProcessError("Invalid auxiliary data") raise wire.ProcessError("Invalid auxiliary data")
def validate(auxiliary_data: messages.CardanoTxAuxiliaryData) -> None: def validate(
auxiliary_data: messages.CardanoTxAuxiliaryData,
protocol_magic: int,
network_id: int,
) -> None:
fields_provided = 0 fields_provided = 0
if auxiliary_data.hash: if auxiliary_data.hash:
fields_provided += 1 fields_provided += 1
@ -47,13 +51,17 @@ def validate(auxiliary_data: messages.CardanoTxAuxiliaryData) -> None:
if auxiliary_data.governance_registration_parameters: if auxiliary_data.governance_registration_parameters:
fields_provided += 1 fields_provided += 1
_validate_governance_registration_parameters( _validate_governance_registration_parameters(
auxiliary_data.governance_registration_parameters auxiliary_data.governance_registration_parameters,
protocol_magic,
network_id,
) )
assert_cond(fields_provided == 1) assert_cond(fields_provided == 1)
def _validate_governance_registration_parameters( def _validate_governance_registration_parameters(
parameters: messages.CardanoGovernanceRegistrationParametersType, parameters: messages.CardanoGovernanceRegistrationParametersType,
protocol_magic: int,
network_id: int,
) -> None: ) -> None:
voting_key_fields_provided = 0 voting_key_fields_provided = 0
if parameters.voting_public_key is not None: if parameters.voting_public_key is not None:
@ -67,9 +75,18 @@ def _validate_governance_registration_parameters(
assert_cond(SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.staking_path)) assert_cond(SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.staking_path))
address_parameters = parameters.reward_address_parameters reward_address_fields_provided = 0
assert_cond(address_parameters.address_type != CardanoAddressType.BYRON) if parameters.reward_address is not None:
addresses.validate_address_parameters(address_parameters) reward_address_fields_provided += 1
addresses.validate_governance_reward_address(
parameters.reward_address, protocol_magic, network_id
)
if parameters.reward_address_parameters:
reward_address_fields_provided += 1
addresses.validate_governance_reward_address_parameters(
parameters.reward_address_parameters
)
assert_cond(reward_address_fields_provided == 1)
if parameters.voting_purpose is not None: if parameters.voting_purpose is not None:
assert_cond(parameters.format == CardanoGovernanceRegistrationFormat.CIP36) assert_cond(parameters.format == CardanoGovernanceRegistrationFormat.CIP36)
@ -144,6 +161,12 @@ async def _show_governance_registration(
bech32.HRP_GOVERNANCE_PUBLIC_KEY, parameters.voting_public_key bech32.HRP_GOVERNANCE_PUBLIC_KEY, parameters.voting_public_key
) )
if parameters.reward_address:
reward_address = parameters.reward_address
else:
assert (
parameters.reward_address_parameters
) # _validate_governance_registration_parameters
reward_address = addresses.derive_human_readable( reward_address = addresses.derive_human_readable(
keychain, keychain,
parameters.reward_address_parameters, parameters.reward_address_parameters,
@ -241,9 +264,14 @@ def _get_signed_governance_registration_payload(
staking_key = derive_public_key(keychain, parameters.staking_path) staking_key = derive_public_key(keychain, parameters.staking_path)
if parameters.reward_address:
reward_address = addresses.get_bytes_unsafe(parameters.reward_address)
else:
address_parameters = parameters.reward_address_parameters
assert address_parameters # _validate_governance_registration_parameters
reward_address = addresses.derive_bytes( reward_address = addresses.derive_bytes(
keychain, keychain,
parameters.reward_address_parameters, address_parameters,
protocol_magic, protocol_magic,
network_id, network_id,
) )

View File

@ -824,7 +824,7 @@ class Signer:
data: messages.CardanoTxAuxiliaryData = await self.ctx.call( data: messages.CardanoTxAuxiliaryData = await self.ctx.call(
CardanoTxItemAck(), messages.CardanoTxAuxiliaryData CardanoTxItemAck(), messages.CardanoTxAuxiliaryData
) )
auxiliary_data.validate(data) auxiliary_data.validate(data, msg.protocol_magic, msg.network_id)
( (
auxiliary_data_hash, auxiliary_data_hash,

View File

@ -1657,22 +1657,24 @@ if TYPE_CHECKING:
class CardanoGovernanceRegistrationParametersType(protobuf.MessageType): class CardanoGovernanceRegistrationParametersType(protobuf.MessageType):
voting_public_key: "bytes | None" voting_public_key: "bytes | None"
staking_path: "list[int]" staking_path: "list[int]"
reward_address_parameters: "CardanoAddressParametersType" reward_address_parameters: "CardanoAddressParametersType | None"
nonce: "int" nonce: "int"
format: "CardanoGovernanceRegistrationFormat" format: "CardanoGovernanceRegistrationFormat"
delegations: "list[CardanoGovernanceRegistrationDelegation]" delegations: "list[CardanoGovernanceRegistrationDelegation]"
voting_purpose: "int | None" voting_purpose: "int | None"
reward_address: "str | None"
def __init__( def __init__(
self, self,
*, *,
reward_address_parameters: "CardanoAddressParametersType",
nonce: "int", nonce: "int",
staking_path: "list[int] | None" = None, staking_path: "list[int] | None" = None,
delegations: "list[CardanoGovernanceRegistrationDelegation] | None" = None, delegations: "list[CardanoGovernanceRegistrationDelegation] | None" = None,
voting_public_key: "bytes | None" = None, voting_public_key: "bytes | None" = None,
reward_address_parameters: "CardanoAddressParametersType | None" = None,
format: "CardanoGovernanceRegistrationFormat | None" = None, format: "CardanoGovernanceRegistrationFormat | None" = None,
voting_purpose: "int | None" = None, voting_purpose: "int | None" = None,
reward_address: "str | None" = None,
) -> None: ) -> None:
pass pass

View File

@ -64,7 +64,6 @@ REQUIRED_FIELDS_TOKEN_GROUP = ("policy_id", "tokens")
REQUIRED_FIELDS_GOVERNANCE_REGISTRATION = ( REQUIRED_FIELDS_GOVERNANCE_REGISTRATION = (
"staking_path", "staking_path",
"nonce", "nonce",
"reward_address_parameters",
) )
REQUIRED_FIELDS_GOVERNANCE_DELEGATION = ("voting_public_key", "weight") REQUIRED_FIELDS_GOVERNANCE_DELEGATION = ("voting_public_key", "weight")
@ -596,10 +595,13 @@ def parse_auxiliary_data(
), ),
staking_path=tools.parse_path(governance_registration["staking_path"]), staking_path=tools.parse_path(governance_registration["staking_path"]),
nonce=governance_registration["nonce"], nonce=governance_registration["nonce"],
reward_address=governance_registration.get("reward_address"),
reward_address_parameters=_parse_address_parameters( reward_address_parameters=_parse_address_parameters(
governance_registration["reward_address_parameters"], governance_registration["reward_address_parameters"],
str(AUXILIARY_DATA_MISSING_FIELDS_ERROR), str(AUXILIARY_DATA_MISSING_FIELDS_ERROR),
), )
if "reward_address_parameters" in governance_registration
else None,
format=serialization_format, format=serialization_format,
delegations=delegations, delegations=delegations,
voting_purpose=voting_purpose, voting_purpose=voting_purpose,

View File

@ -2658,31 +2658,34 @@ class CardanoGovernanceRegistrationParametersType(protobuf.MessageType):
FIELDS = { FIELDS = {
1: protobuf.Field("voting_public_key", "bytes", repeated=False, required=False, default=None), 1: protobuf.Field("voting_public_key", "bytes", repeated=False, required=False, default=None),
2: protobuf.Field("staking_path", "uint32", repeated=True, required=False, default=None), 2: protobuf.Field("staking_path", "uint32", repeated=True, required=False, default=None),
3: protobuf.Field("reward_address_parameters", "CardanoAddressParametersType", repeated=False, required=True), 3: protobuf.Field("reward_address_parameters", "CardanoAddressParametersType", repeated=False, required=False, default=None),
4: protobuf.Field("nonce", "uint64", repeated=False, required=True), 4: protobuf.Field("nonce", "uint64", repeated=False, required=True),
5: protobuf.Field("format", "CardanoGovernanceRegistrationFormat", repeated=False, required=False, default=CardanoGovernanceRegistrationFormat.CIP15), 5: protobuf.Field("format", "CardanoGovernanceRegistrationFormat", repeated=False, required=False, default=CardanoGovernanceRegistrationFormat.CIP15),
6: protobuf.Field("delegations", "CardanoGovernanceRegistrationDelegation", repeated=True, required=False, default=None), 6: protobuf.Field("delegations", "CardanoGovernanceRegistrationDelegation", repeated=True, required=False, default=None),
7: protobuf.Field("voting_purpose", "uint64", repeated=False, required=False, default=None), 7: protobuf.Field("voting_purpose", "uint64", repeated=False, required=False, default=None),
8: protobuf.Field("reward_address", "string", repeated=False, required=False, default=None),
} }
def __init__( def __init__(
self, self,
*, *,
reward_address_parameters: "CardanoAddressParametersType",
nonce: "int", nonce: "int",
staking_path: Optional[Sequence["int"]] = None, staking_path: Optional[Sequence["int"]] = None,
delegations: Optional[Sequence["CardanoGovernanceRegistrationDelegation"]] = None, delegations: Optional[Sequence["CardanoGovernanceRegistrationDelegation"]] = None,
voting_public_key: Optional["bytes"] = None, voting_public_key: Optional["bytes"] = None,
reward_address_parameters: Optional["CardanoAddressParametersType"] = None,
format: Optional["CardanoGovernanceRegistrationFormat"] = CardanoGovernanceRegistrationFormat.CIP15, format: Optional["CardanoGovernanceRegistrationFormat"] = CardanoGovernanceRegistrationFormat.CIP15,
voting_purpose: Optional["int"] = None, voting_purpose: Optional["int"] = None,
reward_address: Optional["str"] = None,
) -> None: ) -> None:
self.staking_path: Sequence["int"] = staking_path if staking_path is not None else [] self.staking_path: Sequence["int"] = staking_path if staking_path is not None else []
self.delegations: Sequence["CardanoGovernanceRegistrationDelegation"] = delegations if delegations is not None else [] self.delegations: Sequence["CardanoGovernanceRegistrationDelegation"] = delegations if delegations is not None else []
self.reward_address_parameters = reward_address_parameters
self.nonce = nonce self.nonce = nonce
self.voting_public_key = voting_public_key self.voting_public_key = voting_public_key
self.reward_address_parameters = reward_address_parameters
self.format = format self.format = format
self.voting_purpose = voting_purpose self.voting_purpose = voting_purpose
self.reward_address = reward_address
class CardanoTxAuxiliaryData(protobuf.MessageType): class CardanoTxAuxiliaryData(protobuf.MessageType):