feat(cardano): add support for CIP36 Catalyst registration format

pull/2592/head
David Misiak 2 years ago committed by matejcik
parent 02dad0225b
commit ab02f7a400

@ -69,6 +69,11 @@ enum CardanoTxAuxiliaryDataSupplementType {
CATALYST_REGISTRATION_SIGNATURE = 1;
}
enum CardanoCatalystRegistrationFormat {
CIP15 = 0;
CIP36 = 1;
}
enum CardanoTxSigningMode {
ORDINARY_TRANSACTION = 0;
POOL_REGISTRATION_AS_OWNER = 1;
@ -332,7 +337,7 @@ message CardanoPoolParametersType {
* Request: Transaction certificate data
* @next CardanoTxItemAck
*/
message CardanoTxCertificate {
message CardanoTxCertificate {
required CardanoCertificateType type = 1; // certificate type
repeated uint32 path = 2; // stake credential key path
optional bytes pool = 3; // pool hash
@ -355,11 +360,22 @@ message CardanoTxWithdrawal {
/**
* @embed
*/
message CardanoCatalystRegistrationParametersType {
message CardanoCatalystRegistrationDelegation {
required bytes voting_public_key = 1;
required uint32 weight = 2;
}
/**
* @embed
*/
message CardanoCatalystRegistrationParametersType {
optional bytes voting_public_key = 1;
repeated uint32 staking_path = 2;
required CardanoAddressParametersType reward_address_parameters = 3;
required uint64 nonce = 4;
optional CardanoCatalystRegistrationFormat format = 5 [default=CIP15];
repeated CardanoCatalystRegistrationDelegation delegations = 6; // mutually exclusive with voting_public_key; max 32 delegations
optional uint64 voting_purpose = 7;
}
/**

@ -1398,6 +1398,63 @@
"error_message": "Invalid auxiliary data"
}
},
{
"description": "transaction with both voting public key and delegations in catalyst registration",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [],
"auxiliary_data": {
"catalyst_registration_parameters": {
"voting_public_key": "38DA0B509D45BF6C87BD55594B92F97081D3923B8C1334B9B8D0BF13FC1C12D0",
"staking_path": "m/1852'/1815'/0'/2/0",
"reward_address_parameters": {
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/0"
},
"nonce": 140,
"format": 1,
"delegations": [
{
"voting_public_key": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 1
}
]
}
},
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid auxiliary data"
}
},
{
"description": "Output datum hash has incorrect length",
"parameters": {

@ -1002,7 +1002,7 @@
}
},
{
"description": "transaction with catalyst registration",
"description": "transaction with CIP15 catalyst registration",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
@ -1064,6 +1064,149 @@
}
}
},
{
"description": "transaction with CIP36 catalyst registration and voting purpose not specified",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [],
"auxiliary_data": {
"catalyst_registration_parameters": {
"staking_path": "m/1852'/1815'/0'/2/0",
"reward_address_parameters": {
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/0"
},
"nonce": 22634813,
"format": 1,
"delegations": [
{
"voting_public_key": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 1
},
{
"voting_public_key": "2af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 2
}
]
}
},
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "15e4e382d913a743776b93d730fee3ca39bfa3ee203801205333bc9aad249612",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "c984c65a5d6ee16c9cdd9fd332a5f64907f25438ef2d1e6d625bdd5c76d15acdf3e5700338b6b5c0ca30d25dd604e1b33ab5ee3459ff8ce3ca5a11e774a18605",
"chain_code": null
}
],
"auxiliary_data_supplement": {
"type": 1,
"auxiliary_data_hash": "9d4c00f5b5b67760931fd7ed9850ff8e14dcdf957685191ab4bc755c52f0ed56",
"catalyst_signature": "2671b8e668ffce235647ac89deda6cc222e7b31a3d44606c2723fcf711b29f9af1e30b0c6b4f87ba37ddf9f6adf0226c39c09e655255890644a3dc4e64c3a001"
}
}
},
{
"description": "transaction with CIP36 catalyst registration and OTHER voting purpose",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [],
"auxiliary_data": {
"catalyst_registration_parameters": {
"staking_path": "m/1852'/1815'/0'/2/0",
"reward_address_parameters": {
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/0"
},
"nonce": 22634813,
"format": 1,
"delegations": [
{
"voting_public_key": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 1
}
],
"voting_purpose": 1
}
},
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "98357cec961c4c2bfef747bb204a06945ab55077166ec4367b644882136b8b39",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "9ac45a56c7002a8bca2121b9f0bae52a7201336b7528495c22d49b845b514d93a70ca1571e8a4dd418fbf4c260018c264843e54fbd2a8c6486e8f00f93cd5103",
"chain_code": null
}
],
"auxiliary_data_supplement": {
"type": 1,
"auxiliary_data_hash": "28b7ffa6800833bdfe5421739eaa21d4a49cde1d84e762b147001169f7c0a385",
"catalyst_signature": "ebc00c615f988c6fc2e132d4419a719f04bbec56fe2569a00746a9e9b0d6e5bdd0809515cb2522c773c991c5ae39834403654d36b37e70b14897c0e98c8c0a0c"
}
}
},
{
"description": "Testnet transaction",
"parameters": {
@ -1592,14 +1735,21 @@
],
"auxiliary_data": {
"catalyst_registration_parameters": {
"voting_public_key": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"staking_path": "m/1852'/1815'/0'/2/0",
"reward_address_parameters": {
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/0"
},
"nonce": 22634813
"nonce": 22634813,
"format": 1,
"delegations": [
{
"voting_public_key": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 1
}
],
"voting_purpose": 0
}
},
"mint": [],
@ -1614,37 +1764,37 @@
"include_network_id": false
},
"result": {
"tx_hash": "ee0dfef8b97857ebe7aa8935af50e9f8f608ff4054c0c034600750d722d90631",
"tx_hash": "f98e1b5edfd376356eb211103bfae679380929bf7fbc40b3355a68e98111d091",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "7d17407e4e8f8b89f8794c022408a84e6f7ef163957d9d7e8ebee4cf9b5c87750c7c559f3a2663441535eec88ebce8540e7d7ea30897de984b1053b818374007",
"signature": "448d2e063f1dbc8662a9f6dea887549cbee7d8e4254124dd1aed08330f4ce165531a846b4ebc42e9944d85b99e878b4255860b960c5f4bd94d4feeb42295d402",
"chain_code": null
},
{
"type": 1,
"pub_key": "36a8ef21d5b98fdf23a27325cf643deaac35e912c835e35037f23d1061ae5b16",
"signature": "df62ec013a32d137c86931cec726d104cbc3193776026ec36d10450d9cbd289abc4c2d44311878b3aba035a8aec2c076522183027f9da046b586b5de5c460504",
"signature": "5ba01fe1a043d3851236395a22982bfdf9d58d80ee963c042e2aa3bc0f8b35b99be18319710ade92edcf49b7185b5e8d91710f3acaa8d9e0f41bad1e3271a801",
"chain_code": null
},
{
"type": 1,
"pub_key": "e90d7b0a6cf831b0042d37961dd528842860e77914e715bcece676c75353b812",
"signature": "e249396d227f1d0540e58b64610bdb990eb1f1db9b3bae4a3d4a8088679af4a3bab464a5c912f7041a5fabc37e3009b3e1f4d76e2406429a0ebed85b880ecd0c",
"signature": "5595ab117629c0a3743e7081b315d937451d546525db43b7253a76662a24100d23baeaf232dc2cccfbdd624ec3439a20a3ca0914b71df0a766ba08f444d1a60d",
"chain_code": null
},
{
"type": 1,
"pub_key": "bc65be1b0b9d7531778a1317c2aa6de936963c3f9ac7d5ee9e9eda25e0c97c5e",
"signature": "0dfd139ce3e255664a77de7d199ce5e4f1a1238ec17a6acec4aaae79be2ccd9b1d21127164c059c8aea2c4b91292aaf352c824550db7594b59e4eca6455d3f03",
"signature": "a130822ccf92dee7a9c357432c7e4b4c6f21fc6efac9c548d00162569bc748b19384ccdf6c132d68b04526658c3766e40cef7b45f73f5398b0db946469343005",
"chain_code": null
}
],
"auxiliary_data_supplement": {
"type": 1,
"auxiliary_data_hash": "a943e9166f1bb6d767b175384d3bd7d23645170df36fc1861fbf344135d8e120",
"catalyst_signature": "74f27d877bbb4a5fc4f7c56869905c11f70bad0af3de24b23afaa1d024e750930f434ecc4b73e5d1723c2cb8548e8bf6098ac876487b3a6ed0891cb76994d409"
"auxiliary_data_hash": "544c9ae849c82e31224865ff936decc6160047409eee4a6b4178b729fe3d286c",
"catalyst_signature": "3064949c9f186138f95e228075d0119dd5cb50e1b7e75d24d569fa547e018a597615da7c79a39ca8e394ee1ba8acb83e70be80f37e69aef3b86e7c4a6bd44903"
}
}
},

@ -455,6 +455,79 @@
}
]
}
},
{
"description": "transaction with CIP36 catalyst registration and voting purpose not specified",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [],
"auxiliary_data": {
"catalyst_registration_parameters": {
"staking_path": "m/1852'/1815'/0'/2/0",
"reward_address_parameters": {
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/0"
},
"nonce": 22634813,
"format": 1,
"delegations": [
{
"voting_public_key": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 1
},
{
"voting_public_key": "2af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"weight": 2
}
]
}
},
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "15e4e382d913a743776b93d730fee3ca39bfa3ee203801205333bc9aad249612",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "c984c65a5d6ee16c9cdd9fd332a5f64907f25438ef2d1e6d625bdd5c76d15acdf3e5700338b6b5c0ca30d25dd604e1b33ab5ee3459ff8ce3ca5a11e774a18605",
"chain_code": null
}
],
"auxiliary_data_supplement": {
"type": 1,
"auxiliary_data_hash": "9d4c00f5b5b67760931fd7ed9850ff8e14dcdf957685191ab4bc755c52f0ed56",
"catalyst_signature": "2671b8e668ffce235647ac89deda6cc222e7b31a3d44606c2723fcf711b29f9af1e30b0c6b4f87ba37ddf9f6adf0226c39c09e655255890644a3dc4e64c3a001"
}
}
}
]
}

@ -419,6 +419,8 @@ if not utils.BITCOIN_ONLY:
import trezor.enums.BinanceTimeInForce
trezor.enums.CardanoAddressType
import trezor.enums.CardanoAddressType
trezor.enums.CardanoCatalystRegistrationFormat
import trezor.enums.CardanoCatalystRegistrationFormat
trezor.enums.CardanoCertificateType
import trezor.enums.CardanoCertificateType
trezor.enums.CardanoDerivationType

@ -3,18 +3,22 @@ from typing import TYPE_CHECKING
from trezor import messages, wire
from trezor.crypto import hashlib
from trezor.crypto.curve import ed25519
from trezor.enums import CardanoAddressType, CardanoTxAuxiliaryDataSupplementType
from trezor.enums import (
CardanoAddressType,
CardanoCatalystRegistrationFormat,
CardanoTxAuxiliaryDataSupplementType,
)
from apps.common import cbor
from . import addresses
from . import addresses, layout
from .helpers import bech32
from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
from .helpers.utils import derive_public_key
from .layout import confirm_catalyst_registration, show_auxiliary_data_hash
if TYPE_CHECKING:
CatalystRegistrationPayload = dict[int, bytes | int]
Delegations = list[tuple[bytes, int]]
CatalystRegistrationPayload = dict[int, Delegations | bytes | int]
SignedCatalystRegistrationPayload = tuple[CatalystRegistrationPayload, bytes]
CatalystRegistrationSignature = dict[int, bytes]
CatalystRegistration = dict[
@ -30,6 +34,14 @@ CATALYST_REGISTRATION_HASH_SIZE = 32
METADATA_KEY_CATALYST_REGISTRATION = 61284
METADATA_KEY_CATALYST_REGISTRATION_SIGNATURE = 61285
MAX_DELEGATION_COUNT = 32
DEFAULT_VOTING_PURPOSE = 0
def assert_cond(condition: bool) -> None:
if not condition:
raise wire.ProcessError("Invalid auxiliary data")
def validate(auxiliary_data: messages.CardanoTxAuxiliaryData) -> None:
fields_provided = 0
@ -41,31 +53,57 @@ def validate(auxiliary_data: messages.CardanoTxAuxiliaryData) -> None:
_validate_catalyst_registration_parameters(
auxiliary_data.catalyst_registration_parameters
)
if fields_provided != 1:
raise wire.ProcessError("Invalid auxiliary data")
assert_cond(fields_provided == 1)
def _validate_hash(auxiliary_data_hash: bytes) -> None:
if len(auxiliary_data_hash) != AUXILIARY_DATA_HASH_SIZE:
raise wire.ProcessError("Invalid auxiliary data")
assert_cond(len(auxiliary_data_hash) == AUXILIARY_DATA_HASH_SIZE)
def _validate_catalyst_registration_parameters(
parameters: messages.CardanoCatalystRegistrationParametersType,
) -> None:
if len(parameters.voting_public_key) != CATALYST_VOTING_PUBLIC_KEY_LENGTH:
raise wire.ProcessError("Invalid auxiliary data")
if not SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.staking_path):
raise wire.ProcessError("Invalid auxiliary data")
voting_key_fields_provided = 0
if parameters.voting_public_key is not None:
voting_key_fields_provided += 1
_validate_voting_public_key(parameters.voting_public_key)
if parameters.delegations:
voting_key_fields_provided += 1
assert_cond(parameters.format == CardanoCatalystRegistrationFormat.CIP36)
_validate_delegations(parameters.delegations)
assert_cond(voting_key_fields_provided == 1)
assert_cond(SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.staking_path))
address_parameters = parameters.reward_address_parameters
if address_parameters.address_type == CardanoAddressType.BYRON:
raise wire.ProcessError("Invalid auxiliary data")
assert_cond(address_parameters.address_type != CardanoAddressType.BYRON)
addresses.validate_address_parameters(address_parameters)
if parameters.voting_purpose is not None:
assert_cond(parameters.format == CardanoCatalystRegistrationFormat.CIP36)
def _validate_voting_public_key(key: bytes) -> None:
assert_cond(len(key) == CATALYST_VOTING_PUBLIC_KEY_LENGTH)
def _validate_delegations(
delegations: list[messages.CardanoCatalystDelegation],
) -> None:
assert_cond(len(delegations) <= MAX_DELEGATION_COUNT)
for delegation in delegations:
_validate_voting_public_key(delegation.voting_public_key)
def _get_voting_purpose_to_serialize(
parameters: messages.CardanoCatalystRegistrationParametersType,
) -> int | None:
if parameters.format == CardanoCatalystRegistrationFormat.CIP15:
return None
if parameters.voting_purpose is None:
return DEFAULT_VOTING_PURPOSE
return parameters.voting_purpose
async def show(
ctx: wire.Context,
@ -83,10 +121,11 @@ async def show(
parameters,
protocol_magic,
network_id,
should_show_details,
)
if should_show_details:
await show_auxiliary_data_hash(ctx, auxiliary_data_hash)
await layout.show_auxiliary_data_hash(ctx, auxiliary_data_hash)
async def _show_catalyst_registration(
@ -95,9 +134,22 @@ async def _show_catalyst_registration(
parameters: messages.CardanoCatalystRegistrationParametersType,
protocol_magic: int,
network_id: int,
should_show_details: bool,
) -> None:
public_key = parameters.voting_public_key
encoded_public_key = bech32.encode(bech32.HRP_JORMUN_PUBLIC_KEY, public_key)
for delegation in parameters.delegations:
encoded_public_key = bech32.encode(
bech32.HRP_JORMUN_PUBLIC_KEY, delegation.voting_public_key
)
await layout.confirm_catalyst_registration_delegation(
ctx, encoded_public_key, delegation.weight
)
encoded_public_key: str | None = None
if parameters.voting_public_key:
encoded_public_key = bech32.encode(
bech32.HRP_JORMUN_PUBLIC_KEY, parameters.voting_public_key
)
reward_address = addresses.derive_human_readable(
keychain,
parameters.reward_address_parameters,
@ -105,12 +157,17 @@ async def _show_catalyst_registration(
network_id,
)
await confirm_catalyst_registration(
voting_purpose: int | None = (
_get_voting_purpose_to_serialize(parameters) if should_show_details else None
)
await layout.confirm_catalyst_registration(
ctx,
encoded_public_key,
parameters.staking_path,
reward_address,
parameters.nonce,
voting_purpose,
)
@ -172,19 +229,36 @@ def _get_signed_catalyst_registration_payload(
protocol_magic: int,
network_id: int,
) -> SignedCatalystRegistrationPayload:
delegations_or_key: Delegations | bytes
if len(parameters.delegations) > 0:
delegations_or_key = [
(delegation.voting_public_key, delegation.weight)
for delegation in parameters.delegations
]
elif parameters.voting_public_key:
delegations_or_key = parameters.voting_public_key
else:
raise RuntimeError # should not be reached - _validate_governance_registration_parameters
staking_key = derive_public_key(keychain, parameters.staking_path)
reward_address = addresses.derive_bytes(
keychain,
parameters.reward_address_parameters,
protocol_magic,
network_id,
)
voting_purpose = _get_voting_purpose_to_serialize(parameters)
payload: CatalystRegistrationPayload = {
1: parameters.voting_public_key,
1: delegations_or_key,
2: staking_key,
3: addresses.derive_bytes(
keychain,
parameters.reward_address_parameters,
protocol_magic,
network_id,
),
3: reward_address,
4: parameters.nonce,
}
if voting_purpose is not None:
payload[5] = voting_purpose
signature = _create_catalyst_registration_payload_signature(
keychain,

@ -750,27 +750,61 @@ def _format_stake_credential(
raise ValueError
async def confirm_catalyst_registration(
async def confirm_catalyst_registration_delegation(
ctx: wire.Context,
public_key: str,
staking_path: list[int],
reward_address: str,
nonce: int,
weight: int,
) -> None:
props: list[PropertyType] = [
("Catalyst voting key registration", None),
("Delegating to:", public_key),
]
if weight is not None:
props.append(("Weight:", str(weight)))
await confirm_properties(
ctx,
"confirm_catalyst_registration",
"confirm_catalyst_registration_delegation",
title="Confirm transaction",
props=[
("Catalyst voting key registration", None),
("Voting public key:", public_key),
props=props,
br_code=ButtonRequestType.Other,
)
async def confirm_catalyst_registration(
ctx: wire.Context,
public_key: str | None,
staking_path: list[int],
reward_address: str,
nonce: int,
voting_purpose: int | None,
) -> None:
props: list[PropertyType] = [("Catalyst voting key registration", None)]
if public_key is not None:
props.append(("Voting public key:", public_key))
props.extend(
[
(
f"Staking key for account {format_account_number(staking_path)}:",
address_n_to_str(staking_path),
),
("Rewards go to:", reward_address),
("Nonce:", str(nonce)),
],
]
)
if voting_purpose is not None:
props.append(
(
"Voting purpose:",
"Catalyst" if voting_purpose == 0 else f"{voting_purpose} (other)",
)
)
await confirm_properties(
ctx,
"confirm_catalyst_registration",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)

@ -0,0 +1,6 @@
# Automatically generated by pb2py
# fmt: off
# isort:skip_file
CIP15 = 0
CIP36 = 1

@ -383,6 +383,10 @@ if TYPE_CHECKING:
NONE = 0
CATALYST_REGISTRATION_SIGNATURE = 1
class CardanoCatalystRegistrationFormat(IntEnum):
CIP15 = 0
CIP36 = 1
class CardanoTxSigningMode(IntEnum):
ORDINARY_TRANSACTION = 0
POOL_REGISTRATION_AS_OWNER = 1

@ -24,6 +24,7 @@ if TYPE_CHECKING:
from trezor.enums import ButtonRequestType # noqa: F401
from trezor.enums import Capability # noqa: F401
from trezor.enums import CardanoAddressType # noqa: F401
from trezor.enums import CardanoCatalystRegistrationFormat # noqa: F401
from trezor.enums import CardanoCertificateType # noqa: F401
from trezor.enums import CardanoDerivationType # noqa: F401
from trezor.enums import CardanoNativeScriptHashDisplayFormat # noqa: F401
@ -1636,19 +1637,41 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["CardanoTxWithdrawal"]:
return isinstance(msg, cls)
class CardanoCatalystRegistrationParametersType(protobuf.MessageType):
class CardanoCatalystRegistrationDelegation(protobuf.MessageType):
voting_public_key: "bytes"
weight: "int"
def __init__(
self,
*,
voting_public_key: "bytes",
weight: "int",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["CardanoCatalystRegistrationDelegation"]:
return isinstance(msg, cls)
class CardanoCatalystRegistrationParametersType(protobuf.MessageType):
voting_public_key: "bytes | None"
staking_path: "list[int]"
reward_address_parameters: "CardanoAddressParametersType"
nonce: "int"
format: "CardanoCatalystRegistrationFormat"
delegations: "list[CardanoCatalystRegistrationDelegation]"
voting_purpose: "int | None"
def __init__(
self,
*,
voting_public_key: "bytes",
reward_address_parameters: "CardanoAddressParametersType",
nonce: "int",
staking_path: "list[int] | None" = None,
delegations: "list[CardanoCatalystRegistrationDelegation] | None" = None,
voting_public_key: "bytes | None" = None,
format: "CardanoCatalystRegistrationFormat | None" = None,
voting_purpose: "int | None" = None,
) -> None:
pass

@ -57,11 +57,11 @@ REQUIRED_FIELDS_POOL_PARAMETERS = (
)
REQUIRED_FIELDS_TOKEN_GROUP = ("policy_id", "tokens")
REQUIRED_FIELDS_CATALYST_REGISTRATION = (
"voting_public_key",
"staking_path",
"nonce",
"reward_address_parameters",
)
REQUIRED_FIELDS_CATALYST_DELEGATION = ("voting_public_key", "weight")
INCOMPLETE_OUTPUT_ERROR_MESSAGE = "The output is missing some fields"
@ -566,10 +566,27 @@ def parse_auxiliary_data(
):
raise AUXILIARY_DATA_MISSING_FIELDS_ERROR
serialization_format = catalyst_registration.get("format")
delegations = []
for delegation in catalyst_registration.get("delegations", []):
if not all(k in delegation for k in REQUIRED_FIELDS_CATALYST_DELEGATION):
raise AUXILIARY_DATA_MISSING_FIELDS_ERROR
delegations.append(
messages.CardanoCatalystRegistrationDelegation(
voting_public_key=bytes.fromhex(delegation["voting_public_key"]),
weight=int(delegation["weight"]),
)
)
voting_purpose = None
if serialization_format == messages.CardanoCatalystRegistrationFormat.CIP36:
voting_purpose = catalyst_registration.get("voting_purpose")
catalyst_registration_parameters = (
messages.CardanoCatalystRegistrationParametersType(
voting_public_key=bytes.fromhex(
catalyst_registration["voting_public_key"]
voting_public_key=parse_optional_bytes(
catalyst_registration.get("voting_public_key")
),
staking_path=tools.parse_path(catalyst_registration["staking_path"]),
nonce=catalyst_registration["nonce"],
@ -577,6 +594,9 @@ def parse_auxiliary_data(
catalyst_registration["reward_address_parameters"],
str(AUXILIARY_DATA_MISSING_FIELDS_ERROR),
),
format=serialization_format,
delegations=delegations,
voting_purpose=voting_purpose,
)
)

@ -408,6 +408,11 @@ class CardanoTxAuxiliaryDataSupplementType(IntEnum):
CATALYST_REGISTRATION_SIGNATURE = 1
class CardanoCatalystRegistrationFormat(IntEnum):
CIP15 = 0
CIP36 = 1
class CardanoTxSigningMode(IntEnum):
ORDINARY_TRANSACTION = 0
POOL_REGISTRATION_AS_OWNER = 1
@ -2626,27 +2631,53 @@ class CardanoTxWithdrawal(protobuf.MessageType):
self.key_hash = key_hash
class CardanoCatalystRegistrationParametersType(protobuf.MessageType):
class CardanoCatalystRegistrationDelegation(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("voting_public_key", "bytes", repeated=False, required=True),
2: protobuf.Field("weight", "uint32", repeated=False, required=True),
}
def __init__(
self,
*,
voting_public_key: "bytes",
weight: "int",
) -> None:
self.voting_public_key = voting_public_key
self.weight = weight
class CardanoCatalystRegistrationParametersType(protobuf.MessageType):
MESSAGE_WIRE_TYPE = None
FIELDS = {
1: protobuf.Field("voting_public_key", "bytes", repeated=False, required=False),
2: protobuf.Field("staking_path", "uint32", repeated=True, required=False),
3: protobuf.Field("reward_address_parameters", "CardanoAddressParametersType", repeated=False, required=True),
4: protobuf.Field("nonce", "uint64", repeated=False, required=True),
5: protobuf.Field("format", "CardanoCatalystRegistrationFormat", repeated=False, required=False),
6: protobuf.Field("delegations", "CardanoCatalystRegistrationDelegation", repeated=True, required=False),
7: protobuf.Field("voting_purpose", "uint64", repeated=False, required=False),
}
def __init__(
self,
*,
voting_public_key: "bytes",
reward_address_parameters: "CardanoAddressParametersType",
nonce: "int",
staking_path: Optional[Sequence["int"]] = None,
delegations: Optional[Sequence["CardanoCatalystRegistrationDelegation"]] = None,
voting_public_key: Optional["bytes"] = None,
format: Optional["CardanoCatalystRegistrationFormat"] = CardanoCatalystRegistrationFormat.CIP15,
voting_purpose: Optional["int"] = None,
) -> None:
self.staking_path: Sequence["int"] = staking_path if staking_path is not None else []
self.voting_public_key = voting_public_key
self.delegations: Sequence["CardanoCatalystRegistrationDelegation"] = 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.format = format
self.voting_purpose = voting_purpose
class CardanoTxAuxiliaryData(protobuf.MessageType):

Loading…
Cancel
Save