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

pull/2868/head
David Misiak 1 year ago committed by matejcik
parent e2416bcec2
commit a94cfa1a13

@ -369,13 +369,14 @@ message CardanoGovernanceRegistrationDelegation {
* @embed
*/
message CardanoGovernanceRegistrationParametersType {
optional bytes voting_public_key = 1;
optional bytes voting_public_key = 1; // mutually exclusive with delegations
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;
optional CardanoGovernanceRegistrationFormat format = 5 [default=CIP15];
repeated CardanoGovernanceRegistrationDelegation delegations = 6; // mutually exclusive with voting_public_key; max 32 delegations
optional uint64 voting_purpose = 7;
optional string reward_address = 8; // mutually exclusive with reward_address_parameters
}
/**

@ -231,6 +231,16 @@ def validate_output_address_parameters(
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:
if not condition:
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:
try:
address_bytes = bech32.decode_unsafe(address)

@ -2,7 +2,7 @@ from micropython import const
from typing import TYPE_CHECKING
from trezor.crypto import hashlib
from trezor.enums import CardanoAddressType, CardanoGovernanceRegistrationFormat
from trezor.enums import CardanoGovernanceRegistrationFormat
from apps.common import cbor
@ -38,7 +38,11 @@ def assert_cond(condition: bool) -> None:
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
if auxiliary_data.hash:
fields_provided += 1
@ -47,13 +51,17 @@ def validate(auxiliary_data: messages.CardanoTxAuxiliaryData) -> None:
if auxiliary_data.governance_registration_parameters:
fields_provided += 1
_validate_governance_registration_parameters(
auxiliary_data.governance_registration_parameters
auxiliary_data.governance_registration_parameters,
protocol_magic,
network_id,
)
assert_cond(fields_provided == 1)
def _validate_governance_registration_parameters(
parameters: messages.CardanoGovernanceRegistrationParametersType,
protocol_magic: int,
network_id: int,
) -> None:
voting_key_fields_provided = 0
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))
address_parameters = parameters.reward_address_parameters
assert_cond(address_parameters.address_type != CardanoAddressType.BYRON)
addresses.validate_address_parameters(address_parameters)
reward_address_fields_provided = 0
if parameters.reward_address is not None:
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:
assert_cond(parameters.format == CardanoGovernanceRegistrationFormat.CIP36)
@ -144,12 +161,18 @@ async def _show_governance_registration(
bech32.HRP_GOVERNANCE_PUBLIC_KEY, parameters.voting_public_key
)
reward_address = addresses.derive_human_readable(
keychain,
parameters.reward_address_parameters,
protocol_magic,
network_id,
)
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(
keychain,
parameters.reward_address_parameters,
protocol_magic,
network_id,
)
voting_purpose: int | None = (
_get_voting_purpose_to_serialize(parameters) if should_show_details else None
@ -241,12 +264,17 @@ def _get_signed_governance_registration_payload(
staking_key = derive_public_key(keychain, parameters.staking_path)
reward_address = addresses.derive_bytes(
keychain,
parameters.reward_address_parameters,
protocol_magic,
network_id,
)
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(
keychain,
address_parameters,
protocol_magic,
network_id,
)
voting_purpose = _get_voting_purpose_to_serialize(parameters)

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

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

@ -64,7 +64,6 @@ REQUIRED_FIELDS_TOKEN_GROUP = ("policy_id", "tokens")
REQUIRED_FIELDS_GOVERNANCE_REGISTRATION = (
"staking_path",
"nonce",
"reward_address_parameters",
)
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"]),
nonce=governance_registration["nonce"],
reward_address=governance_registration.get("reward_address"),
reward_address_parameters=_parse_address_parameters(
governance_registration["reward_address_parameters"],
str(AUXILIARY_DATA_MISSING_FIELDS_ERROR),
),
)
if "reward_address_parameters" in governance_registration
else None,
format=serialization_format,
delegations=delegations,
voting_purpose=voting_purpose,

@ -2658,31 +2658,34 @@ class CardanoGovernanceRegistrationParametersType(protobuf.MessageType):
FIELDS = {
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),
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),
5: protobuf.Field("format", "CardanoGovernanceRegistrationFormat", repeated=False, required=False, default=CardanoGovernanceRegistrationFormat.CIP15),
6: protobuf.Field("delegations", "CardanoGovernanceRegistrationDelegation", repeated=True, 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__(
self,
*,
reward_address_parameters: "CardanoAddressParametersType",
nonce: "int",
staking_path: Optional[Sequence["int"]] = None,
delegations: Optional[Sequence["CardanoGovernanceRegistrationDelegation"]] = None,
voting_public_key: Optional["bytes"] = None,
reward_address_parameters: Optional["CardanoAddressParametersType"] = None,
format: Optional["CardanoGovernanceRegistrationFormat"] = CardanoGovernanceRegistrationFormat.CIP15,
voting_purpose: Optional["int"] = None,
reward_address: Optional["str"] = None,
) -> None:
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.reward_address_parameters = reward_address_parameters
self.nonce = nonce
self.voting_public_key = voting_public_key
self.reward_address_parameters = reward_address_parameters
self.format = format
self.voting_purpose = voting_purpose
self.reward_address = reward_address
class CardanoTxAuxiliaryData(protobuf.MessageType):

Loading…
Cancel
Save