mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-17 21:22:10 +00:00
feat(cardano): add support for CIP36 Catalyst registration format
This commit is contained in:
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": {
|
||||
|
170
common/tests/fixtures/cardano/sign_tx.json
vendored
170
common/tests/fixtures/cardano/sign_tx.json
vendored
@ -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")
|
||||
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)
|
||||
|
||||
if not SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.staking_path):
|
||||
raise wire.ProcessError("Invalid auxiliary data")
|
||||
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,
|
||||
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_delegation",
|
||||
title="Confirm transaction",
|
||||
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:
|
||||
await confirm_properties(
|
||||
ctx,
|
||||
"confirm_catalyst_registration",
|
||||
title="Confirm transaction",
|
||||
props=[
|
||||
("Catalyst voting key registration", None),
|
||||
("Voting public key:", public_key),
|
||||
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"
|
||||
staking_path: "list[int]"
|
||||
reward_address_parameters: "CardanoAddressParametersType"
|
||||
nonce: "int"
|
||||
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,
|
||||
*,
|
||||
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("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),
|
||||
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,
|
||||
*,
|
||||
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…
Reference in New Issue
Block a user