From a0470b99298e81858e4202fc030ed95f9672c6ec Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 27 Sep 2022 11:48:21 +0200 Subject: [PATCH 1/6] feat(cardano): add support for CIP36 Catalyst registration format --- .../__fixtures__/cardanoSignTransaction.ts | 143 ++++++++++++++++-- .../src/api/cardano/cardanoAuxiliaryData.ts | 35 ++++- .../connect/src/api/cardanoSignTransaction.ts | 13 ++ .../src/types/api/__tests__/cardano.ts | 9 ++ .../connect/src/types/api/cardano/index.ts | 10 +- packages/transport/messages.json | 37 ++++- .../scripts/protobuf-patches/index.js | 3 + packages/transport/src/types/messages.ts | 17 ++- 8 files changed, 252 insertions(+), 15 deletions(-) diff --git a/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts b/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts index 13333a6cb0b..1dfb4c5c73a 100644 --- a/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts +++ b/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts @@ -1,6 +1,7 @@ import { NETWORK_IDS, PROTOCOL_MAGICS } from '@trezor/connect/lib/constants/cardano'; import { CardanoAddressType, + CardanoCatalystRegistrationFormat, CardanoCertificateType, CardanoTxOutputSerializationFormat, CardanoTxSigningMode, @@ -415,6 +416,11 @@ const legacyResults = { rules: ['<2.5.2', '1'], payload: false, }, + beforeCatalystRegistrationCIP36: { + // older FW doesn't support CIP36 Catalyst registration format + rules: ['<2.5.3', '1'], + payload: false, + }, }; export default { @@ -918,6 +924,116 @@ export default { legacyResults: [legacyResults.beforeTransactionStreaming], }, + { + description: 'signTransactionWithCIP36CatalystRegistrationAndVotingPurposeNotSpecified', + params: { + inputs: [SAMPLE_INPUTS.shelley_input], + outputs: [SAMPLE_OUTPUTS.simple_shelley_output], + fee: FEE, + ttl: TTL, + auxiliaryData: { + catalystRegistrationParameters: { + stakingPath: "m/1852'/1815'/0'/2/0", + rewardAddressParameters: { + addressType: CardanoAddressType.BASE, + path: "m/1852'/1815'/0'/0/0", + stakingPath: "m/1852'/1815'/0'/2/0", + }, + nonce: '22634813', + format: CardanoCatalystRegistrationFormat.CIP36, + delegations: [ + { + votingPublicKey: + '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', + weight: 1, + }, + { + votingPublicKey: + '2af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', + weight: 2, + }, + ], + }, + }, + protocolMagic: PROTOCOL_MAGICS.mainnet, + networkId: NETWORK_IDS.mainnet, + signingMode: CardanoTxSigningMode.ORDINARY_TRANSACTION, + }, + result: { + hash: '15e4e382d913a743776b93d730fee3ca39bfa3ee203801205333bc9aad249612', + witnesses: [ + { + type: 1, + pubKey: '5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1', + signature: + 'c984c65a5d6ee16c9cdd9fd332a5f64907f25438ef2d1e6d625bdd5c76d15acdf3e5700338b6b5c0ca30d25dd604e1b33ab5ee3459ff8ce3ca5a11e774a18605', + chainCode: null, + }, + ], + auxiliaryDataSupplement: { + type: 1, + auxiliaryDataHash: + '9d4c00f5b5b67760931fd7ed9850ff8e14dcdf957685191ab4bc755c52f0ed56', + catalystSignature: + '2671b8e668ffce235647ac89deda6cc222e7b31a3d44606c2723fcf711b29f9af1e30b0c6b4f87ba37ddf9f6adf0226c39c09e655255890644a3dc4e64c3a001', + }, + }, + legacyResults: [legacyResults.beforeCatalystRegistrationCIP36], + }, + + { + description: 'signTransactionWithCIP36CatalystRegistrationAndOtherVotingPurpose', + params: { + inputs: [SAMPLE_INPUTS.shelley_input], + outputs: [SAMPLE_OUTPUTS.simple_shelley_output], + fee: FEE, + ttl: TTL, + auxiliaryData: { + catalystRegistrationParameters: { + stakingPath: "m/1852'/1815'/0'/2/0", + rewardAddressParameters: { + addressType: CardanoAddressType.BASE, + path: "m/1852'/1815'/0'/0/0", + stakingPath: "m/1852'/1815'/0'/2/0", + }, + nonce: '22634813', + format: CardanoCatalystRegistrationFormat.CIP36, + delegations: [ + { + votingPublicKey: + '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', + weight: 1, + }, + ], + votingPurpose: 1, + }, + }, + protocolMagic: PROTOCOL_MAGICS.mainnet, + networkId: NETWORK_IDS.mainnet, + signingMode: CardanoTxSigningMode.ORDINARY_TRANSACTION, + }, + result: { + hash: '98357cec961c4c2bfef747bb204a06945ab55077166ec4367b644882136b8b39', + witnesses: [ + { + type: 1, + pubKey: '5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1', + signature: + '9ac45a56c7002a8bca2121b9f0bae52a7201336b7528495c22d49b845b514d93a70ca1571e8a4dd418fbf4c260018c264843e54fbd2a8c6486e8f00f93cd5103', + chainCode: null, + }, + ], + auxiliaryDataSupplement: { + type: 1, + auxiliaryDataHash: + '28b7ffa6800833bdfe5421739eaa21d4a49cde1d84e762b147001169f7c0a385', + catalystSignature: + 'ebc00c615f988c6fc2e132d4419a719f04bbec56fe2569a00746a9e9b0d6e5bdd0809515cb2522c773c991c5ae39834403654d36b37e70b14897c0e98c8c0a0c', + }, + }, + legacyResults: [legacyResults.beforeCatalystRegistrationCIP36], + }, + { description: 'signOutputWithDatumHash', params: { @@ -1603,8 +1719,6 @@ export default { withdrawals: [SAMPLE_WITHDRAWALS.basic], auxiliaryData: { catalystRegistrationParameters: { - votingPublicKey: - '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', stakingPath: "m/1852'/1815'/0'/2/0", rewardAddressParameters: { addressType: CardanoAddressType.BASE, @@ -1612,6 +1726,15 @@ export default { stakingPath: "m/1852'/1815'/0'/2/0", }, nonce: '22634813', + format: CardanoCatalystRegistrationFormat.CIP36, + delegations: [ + { + votingPublicKey: + '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', + weight: 1, + }, + ], + votingPurpose: 0, }, }, protocolMagic: PROTOCOL_MAGICS.mainnet, @@ -1619,46 +1742,46 @@ export default { signingMode: CardanoTxSigningMode.ORDINARY_TRANSACTION, }, result: { - hash: 'ee0dfef8b97857ebe7aa8935af50e9f8f608ff4054c0c034600750d722d90631', + hash: 'f98e1b5edfd376356eb211103bfae679380929bf7fbc40b3355a68e98111d091', witnesses: [ { type: 1, pubKey: '5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1', signature: - '7d17407e4e8f8b89f8794c022408a84e6f7ef163957d9d7e8ebee4cf9b5c87750c7c559f3a2663441535eec88ebce8540e7d7ea30897de984b1053b818374007', + '448d2e063f1dbc8662a9f6dea887549cbee7d8e4254124dd1aed08330f4ce165531a846b4ebc42e9944d85b99e878b4255860b960c5f4bd94d4feeb42295d402', chainCode: null, }, { type: 1, pubKey: '36a8ef21d5b98fdf23a27325cf643deaac35e912c835e35037f23d1061ae5b16', signature: - 'df62ec013a32d137c86931cec726d104cbc3193776026ec36d10450d9cbd289abc4c2d44311878b3aba035a8aec2c076522183027f9da046b586b5de5c460504', + '5ba01fe1a043d3851236395a22982bfdf9d58d80ee963c042e2aa3bc0f8b35b99be18319710ade92edcf49b7185b5e8d91710f3acaa8d9e0f41bad1e3271a801', chainCode: null, }, { type: 1, pubKey: 'e90d7b0a6cf831b0042d37961dd528842860e77914e715bcece676c75353b812', signature: - 'e249396d227f1d0540e58b64610bdb990eb1f1db9b3bae4a3d4a8088679af4a3bab464a5c912f7041a5fabc37e3009b3e1f4d76e2406429a0ebed85b880ecd0c', + '5595ab117629c0a3743e7081b315d937451d546525db43b7253a76662a24100d23baeaf232dc2cccfbdd624ec3439a20a3ca0914b71df0a766ba08f444d1a60d', chainCode: null, }, { type: 1, pubKey: 'bc65be1b0b9d7531778a1317c2aa6de936963c3f9ac7d5ee9e9eda25e0c97c5e', signature: - '0dfd139ce3e255664a77de7d199ce5e4f1a1238ec17a6acec4aaae79be2ccd9b1d21127164c059c8aea2c4b91292aaf352c824550db7594b59e4eca6455d3f03', + 'a130822ccf92dee7a9c357432c7e4b4c6f21fc6efac9c548d00162569bc748b19384ccdf6c132d68b04526658c3766e40cef7b45f73f5398b0db946469343005', chainCode: null, }, ], auxiliaryDataSupplement: { type: 1, auxiliaryDataHash: - 'a943e9166f1bb6d767b175384d3bd7d23645170df36fc1861fbf344135d8e120', + '544c9ae849c82e31224865ff936decc6160047409eee4a6b4178b729fe3d286c', catalystSignature: - '74f27d877bbb4a5fc4f7c56869905c11f70bad0af3de24b23afaa1d024e750930f434ecc4b73e5d1723c2cb8548e8bf6098ac876487b3a6ed0891cb76994d409', + '3064949c9f186138f95e228075d0119dd5cb50e1b7e75d24d569fa547e018a597615da7c79a39ca8e394ee1ba8acb83e70be80f37e69aef3b86e7c4a6bd44903', }, }, - legacyResults: [legacyResults.beforeTransactionStreaming], + legacyResults: [legacyResults.beforeCatalystRegistrationCIP36], }, { diff --git a/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts b/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts index bde4c0eda37..bb6a7e495af 100644 --- a/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts +++ b/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts @@ -8,22 +8,50 @@ import { import { validateParams } from '../common/paramsValidator'; import { validatePath } from '../../utils/pathUtils'; import type { Device } from '../../device/Device'; -import type { PROTO } from '../../constants'; +import { ERRORS, PROTO } from '../../constants'; import type { CardanoAuxiliaryData, + CardanoCatalystRegistrationDelegation, CardanoCatalystRegistrationParameters, } from '../../types/api/cardano'; +const MAX_DELEGATION_COUNT = 32; + +const transformDelegation = ( + delegation: CardanoCatalystRegistrationDelegation, +): PROTO.CardanoCatalystRegistrationDelegation => { + validateParams(delegation, [ + { name: 'votingPublicKey', type: 'string', required: true }, + { name: 'weight', type: 'uint', required: true }, + ]); + + return { + voting_public_key: delegation.votingPublicKey, + weight: delegation.weight, + }; +}; + const transformCatalystRegistrationParameters = ( catalystRegistrationParameters: CardanoCatalystRegistrationParameters, ): PROTO.CardanoCatalystRegistrationParametersType => { validateParams(catalystRegistrationParameters, [ - { name: 'votingPublicKey', type: 'string', required: true }, + { name: 'votingPublicKey', type: 'string' }, { name: 'stakingPath', required: true }, { name: 'nonce', type: 'uint', required: true }, + { name: 'format', type: 'number' }, + { name: 'delegations', type: 'array', allowEmpty: true }, + { name: 'votingPurpose', type: 'uint' }, ]); validateAddressParameters(catalystRegistrationParameters.rewardAddressParameters); + const { delegations } = catalystRegistrationParameters; + if (delegations && delegations.length > MAX_DELEGATION_COUNT) { + throw ERRORS.TypedError( + 'Method_InvalidParameter', + `At most ${MAX_DELEGATION_COUNT} delegations are allowed in a Catalyst registration`, + ); + } + return { voting_public_key: catalystRegistrationParameters.votingPublicKey, staking_path: validatePath(catalystRegistrationParameters.stakingPath, 3), @@ -31,6 +59,9 @@ const transformCatalystRegistrationParameters = ( catalystRegistrationParameters.rewardAddressParameters, ), nonce: catalystRegistrationParameters.nonce, + format: catalystRegistrationParameters.format, + delegations: delegations?.map(transformDelegation), + voting_purpose: catalystRegistrationParameters.votingPurpose, }; }; diff --git a/packages/connect/src/api/cardanoSignTransaction.ts b/packages/connect/src/api/cardanoSignTransaction.ts index 57ef316617e..33276ec58eb 100644 --- a/packages/connect/src/api/cardanoSignTransaction.ts +++ b/packages/connect/src/api/cardanoSignTransaction.ts @@ -44,6 +44,7 @@ const CardanoSignTransactionFeatures = Object.freeze({ Plutus: ['0', '2.4.4'], KeyHashStakeCredential: ['0', '2.4.4'], Babbage: ['0', '2.5.2'], + CatalystRegistrationCIP36: ['0', '2.5.3'], }); export type CardanoSignTransactionParams = { @@ -309,6 +310,18 @@ export default class CardanoSignTransaction extends AbstractMethod< ) { this._ensureFeatureIsSupported('Babbage'); } + + if (params.auxiliaryData?.catalyst_registration_parameters) { + const { format, delegations, voting_purpose } = + params.auxiliaryData.catalyst_registration_parameters; + if ( + format === PROTO.CardanoCatalystRegistrationFormat.CIP36 || + delegations?.length || + voting_purpose != null + ) { + this._ensureFeatureIsSupported('CatalystRegistrationCIP36'); + } + } } async _sign_tx(): Promise { diff --git a/packages/connect/src/types/api/__tests__/cardano.ts b/packages/connect/src/types/api/__tests__/cardano.ts index 3e65ce76409..4cea8036497 100644 --- a/packages/connect/src/types/api/__tests__/cardano.ts +++ b/packages/connect/src/types/api/__tests__/cardano.ts @@ -2,6 +2,7 @@ import { TrezorConnect, PROTO } from '../../..'; const { CardanoAddressType, + CardanoCatalystRegistrationFormat, CardanoCertificateType, CardanoNativeScriptHashDisplayFormat, CardanoNativeScriptType, @@ -336,6 +337,14 @@ export const cardanoSignTransaction = async (api: TrezorConnect) => { }, }, nonce: '0', + format: CardanoCatalystRegistrationFormat.CIP36, + delegations: [ + { + votingPublicKey: 'aaff00..', + weight: 1, + }, + ], + votingPurpose: 0, }, }, additionalWitnessRequests: ['m/44'], diff --git a/packages/connect/src/types/api/cardano/index.ts b/packages/connect/src/types/api/cardano/index.ts index 7d1f5b4fefe..4d5328fda86 100644 --- a/packages/connect/src/types/api/cardano/index.ts +++ b/packages/connect/src/types/api/cardano/index.ts @@ -173,11 +173,19 @@ export interface CardanoReferenceInput { prev_index: number; } -export interface CardanoCatalystRegistrationParameters { +export interface CardanoCatalystRegistrationDelegation { votingPublicKey: string; + weight: number; +} + +export interface CardanoCatalystRegistrationParameters { + votingPublicKey?: string; stakingPath: string | number[]; rewardAddressParameters: CardanoAddressParameters; nonce: string; + format?: PROTO.CardanoCatalystRegistrationFormat; + delegations?: CardanoCatalystRegistrationDelegation[]; + votingPurpose?: number; } export interface CardanoAuxiliaryData { diff --git a/packages/transport/messages.json b/packages/transport/messages.json index 86686c1e43c..ebd4d2bf0a0 100644 --- a/packages/transport/messages.json +++ b/packages/transport/messages.json @@ -1637,6 +1637,12 @@ "CATALYST_REGISTRATION_SIGNATURE": 1 } }, + "CardanoCatalystRegistrationFormat": { + "values": { + "CIP15": 0, + "CIP36": 1 + } + }, "CardanoTxSigningMode": { "values": { "ORDINARY_TRANSACTION": 0, @@ -2239,13 +2245,26 @@ } } }, - "CardanoCatalystRegistrationParametersType": { + "CardanoCatalystRegistrationDelegation": { "fields": { "voting_public_key": { "rule": "required", "type": "bytes", "id": 1 }, + "weight": { + "rule": "required", + "type": "uint32", + "id": 2 + } + } + }, + "CardanoCatalystRegistrationParametersType": { + "fields": { + "voting_public_key": { + "type": "bytes", + "id": 1 + }, "staking_path": { "rule": "repeated", "type": "uint32", @@ -2263,6 +2282,22 @@ "rule": "required", "type": "uint64", "id": 4 + }, + "format": { + "type": "CardanoCatalystRegistrationFormat", + "id": 5, + "options": { + "default": "CIP15" + } + }, + "delegations": { + "rule": "repeated", + "type": "CardanoCatalystRegistrationDelegation", + "id": 6 + }, + "voting_purpose": { + "type": "uint64", + "id": 7 } } }, diff --git a/packages/transport/scripts/protobuf-patches/index.js b/packages/transport/scripts/protobuf-patches/index.js index 6fe5f4b8fbe..b815ce466c8 100644 --- a/packages/transport/scripts/protobuf-patches/index.js +++ b/packages/transport/scripts/protobuf-patches/index.js @@ -20,6 +20,7 @@ const RULE_PATCH = { 'CardanoNativeScript.scripts': 'optional', 'CardanoNativeScript.key_path': 'optional', 'CardanoTxRequiredSigner.key_path': 'optional', + 'CardanoCatalystRegistrationParametersType.delegations': 'optional', 'Success.message': 'required', // didn't find use case where it's not sent 'SignedIdentity.address': 'required', 'EosAuthorizationKey.key': 'required', // its valid to be undefined according to implementation/tests @@ -111,7 +112,9 @@ const TYPE_PATCH = { 'Features.experimental_features': 'boolean | null', 'HDNodePathType.node': 'HDNodeType | string', 'FirmwareUpload.payload': 'Buffer | ArrayBuffer', + 'CardanoCatalystRegistrationDelegation.weight': UINT_TYPE, 'CardanoCatalystRegistrationParametersType.nonce': UINT_TYPE, + 'CardanoCatalystRegistrationParametersType.voting_purpose': UINT_TYPE, 'CardanoPoolParametersType.pledge': UINT_TYPE, 'CardanoPoolParametersType.cost': UINT_TYPE, 'CardanoPoolParametersType.margin_numerator': UINT_TYPE, diff --git a/packages/transport/src/types/messages.ts b/packages/transport/src/types/messages.ts index 106fa4095a5..b9c6a725f21 100644 --- a/packages/transport/src/types/messages.ts +++ b/packages/transport/src/types/messages.ts @@ -627,6 +627,11 @@ export enum CardanoTxAuxiliaryDataSupplementType { CATALYST_REGISTRATION_SIGNATURE = 1, } +export enum CardanoCatalystRegistrationFormat { + CIP15 = 0, + CIP36 = 1, +} + export enum CardanoTxSigningMode { ORDINARY_TRANSACTION = 0, POOL_REGISTRATION_AS_OWNER = 1, @@ -826,12 +831,21 @@ export type CardanoTxWithdrawal = { key_hash?: string; }; +// CardanoCatalystRegistrationDelegation +export type CardanoCatalystRegistrationDelegation = { + voting_public_key: string; + weight: UintType; +}; + // CardanoCatalystRegistrationParametersType export type CardanoCatalystRegistrationParametersType = { - voting_public_key: string; + voting_public_key?: string; staking_path: number[]; reward_address_parameters: CardanoAddressParametersType; nonce: UintType; + format?: CardanoCatalystRegistrationFormat; + delegations?: CardanoCatalystRegistrationDelegation[]; + voting_purpose?: UintType; }; // CardanoTxAuxiliaryData @@ -2241,6 +2255,7 @@ export type MessageType = { CardanoPoolParametersType: CardanoPoolParametersType; CardanoTxCertificate: CardanoTxCertificate; CardanoTxWithdrawal: CardanoTxWithdrawal; + CardanoCatalystRegistrationDelegation: CardanoCatalystRegistrationDelegation; CardanoCatalystRegistrationParametersType: CardanoCatalystRegistrationParametersType; CardanoTxAuxiliaryData: CardanoTxAuxiliaryData; CardanoTxMint: CardanoTxMint; From 9137e42f92b45dff122f984e43498a3f7c3ea0c8 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 27 Sep 2022 17:41:14 +0200 Subject: [PATCH 2/6] chore(cardano): rename Catalyst to governance --- .../connect/methods/cardanoSignTransaction.md | 6 +-- .../__fixtures__/cardanoSignTransaction.ts | 47 ++++++++-------- .../src/api/cardano/cardanoAuxiliaryData.ts | 54 +++++++++---------- .../connect/src/api/cardanoSignTransaction.ts | 16 +++--- .../src/types/api/__tests__/cardano.ts | 10 ++-- .../connect/src/types/api/cardano/index.ts | 12 ++--- packages/transport/messages.json | 18 +++---- .../scripts/protobuf-patches/index.js | 8 +-- packages/transport/src/types/messages.ts | 24 ++++----- 9 files changed, 98 insertions(+), 97 deletions(-) diff --git a/docs/packages/connect/methods/cardanoSignTransaction.md b/docs/packages/connect/methods/cardanoSignTransaction.md index f49781d9bda..0fc4e23aeac 100644 --- a/docs/packages/connect/methods/cardanoSignTransaction.md +++ b/docs/packages/connect/methods/cardanoSignTransaction.md @@ -275,7 +275,7 @@ TrezorConnect.cardanoSignTransaction({ }); ``` -#### Catalyst voting key registration +#### Governance voting key registration ```javascript TrezorConnect.cardanoSignTransaction({ @@ -297,7 +297,7 @@ TrezorConnect.cardanoSignTransaction({ fee: '42', ttl: '10', auxiliaryData: { - catalystRegistrationParameters: { + governanceRegistrationParameters: { votingPublicKey: '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', stakingPath: "m/1852'/1815'/0'/2/0", rewardAddressParameters: { @@ -568,7 +568,7 @@ Example: type: 1, auxiliaryDataHash: 'a943e9166f1bb6d767b175384d3bd7d23645170df36fc1861fbf344135d8e120', - catalystSignature: + governanceSignature: '74f27d877bbb4a5fc4f7c56869905c11f70bad0af3de24b23afaa1d024e750930f434ecc4b73e5d1723c2cb8548e8bf6098ac876487b3a6ed0891cb76994d409', }, } diff --git a/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts b/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts index 1dfb4c5c73a..df2bf2f959f 100644 --- a/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts +++ b/packages/connect/e2e/__fixtures__/cardanoSignTransaction.ts @@ -1,7 +1,7 @@ import { NETWORK_IDS, PROTOCOL_MAGICS } from '@trezor/connect/lib/constants/cardano'; import { CardanoAddressType, - CardanoCatalystRegistrationFormat, + CardanoGovernanceRegistrationFormat, CardanoCertificateType, CardanoTxOutputSerializationFormat, CardanoTxSigningMode, @@ -416,8 +416,8 @@ const legacyResults = { rules: ['<2.5.2', '1'], payload: false, }, - beforeCatalystRegistrationCIP36: { - // older FW doesn't support CIP36 Catalyst registration format + beforeGovernanceRegistrationCIP36: { + // older FW doesn't support CIP36 governance registration format rules: ['<2.5.3', '1'], payload: false, }, @@ -835,14 +835,14 @@ export default { }, { - description: 'signCatalystVotingRegistrationWithPath', + description: 'signGovernanceVotingRegistrationWithPath', params: { inputs: [SAMPLE_INPUTS.shelley_input], outputs: [SAMPLE_OUTPUTS.simple_shelley_output], fee: FEE, ttl: TTL, auxiliaryData: { - catalystRegistrationParameters: { + governanceRegistrationParameters: { votingPublicKey: '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', stakingPath: "m/1852'/1815'/0'/2/0", @@ -872,7 +872,7 @@ export default { type: 1, auxiliaryDataHash: 'b712ad07750007ba68d7558abeeab103b36a09133062ba9fa6611953085d9137', - catalystSignature: + governanceSignature: 'ed3335aead65c665ceee21f2549c0ef4c9137b94c13fa642bea4a2c24e44e7f1ee06b47e14151efcf8d5569a404260c01f277b3ba516b5826a15c8ba2c97f70c', }, }, @@ -880,14 +880,14 @@ export default { }, { - description: 'signCatalystVotingRegistrationWithStakingPath', + description: 'signGovernanceVotingRegistrationWithStakingPath', params: { inputs: [SAMPLE_INPUTS.shelley_input], outputs: [SAMPLE_OUTPUTS.simple_shelley_output], fee: FEE, ttl: TTL, auxiliaryData: { - catalystRegistrationParameters: { + governanceRegistrationParameters: { votingPublicKey: '1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc', stakingPath: "m/1852'/1815'/0'/2/0", @@ -917,7 +917,7 @@ export default { type: 1, auxiliaryDataHash: 'b712ad07750007ba68d7558abeeab103b36a09133062ba9fa6611953085d9137', - catalystSignature: + governanceSignature: 'ed3335aead65c665ceee21f2549c0ef4c9137b94c13fa642bea4a2c24e44e7f1ee06b47e14151efcf8d5569a404260c01f277b3ba516b5826a15c8ba2c97f70c', }, }, @@ -925,14 +925,15 @@ export default { }, { - description: 'signTransactionWithCIP36CatalystRegistrationAndVotingPurposeNotSpecified', + description: + 'signTransactionWithCIP36GovernanceRegistrationAndVotingPurposeNotSpecified', params: { inputs: [SAMPLE_INPUTS.shelley_input], outputs: [SAMPLE_OUTPUTS.simple_shelley_output], fee: FEE, ttl: TTL, auxiliaryData: { - catalystRegistrationParameters: { + governanceRegistrationParameters: { stakingPath: "m/1852'/1815'/0'/2/0", rewardAddressParameters: { addressType: CardanoAddressType.BASE, @@ -940,7 +941,7 @@ export default { stakingPath: "m/1852'/1815'/0'/2/0", }, nonce: '22634813', - format: CardanoCatalystRegistrationFormat.CIP36, + format: CardanoGovernanceRegistrationFormat.CIP36, delegations: [ { votingPublicKey: @@ -974,22 +975,22 @@ export default { type: 1, auxiliaryDataHash: '9d4c00f5b5b67760931fd7ed9850ff8e14dcdf957685191ab4bc755c52f0ed56', - catalystSignature: + governanceSignature: '2671b8e668ffce235647ac89deda6cc222e7b31a3d44606c2723fcf711b29f9af1e30b0c6b4f87ba37ddf9f6adf0226c39c09e655255890644a3dc4e64c3a001', }, }, - legacyResults: [legacyResults.beforeCatalystRegistrationCIP36], + legacyResults: [legacyResults.beforeGovernanceRegistrationCIP36], }, { - description: 'signTransactionWithCIP36CatalystRegistrationAndOtherVotingPurpose', + description: 'signTransactionWithCIP36GovernanceRegistrationAndOtherVotingPurpose', params: { inputs: [SAMPLE_INPUTS.shelley_input], outputs: [SAMPLE_OUTPUTS.simple_shelley_output], fee: FEE, ttl: TTL, auxiliaryData: { - catalystRegistrationParameters: { + governanceRegistrationParameters: { stakingPath: "m/1852'/1815'/0'/2/0", rewardAddressParameters: { addressType: CardanoAddressType.BASE, @@ -997,7 +998,7 @@ export default { stakingPath: "m/1852'/1815'/0'/2/0", }, nonce: '22634813', - format: CardanoCatalystRegistrationFormat.CIP36, + format: CardanoGovernanceRegistrationFormat.CIP36, delegations: [ { votingPublicKey: @@ -1027,11 +1028,11 @@ export default { type: 1, auxiliaryDataHash: '28b7ffa6800833bdfe5421739eaa21d4a49cde1d84e762b147001169f7c0a385', - catalystSignature: + governanceSignature: 'ebc00c615f988c6fc2e132d4419a719f04bbec56fe2569a00746a9e9b0d6e5bdd0809515cb2522c773c991c5ae39834403654d36b37e70b14897c0e98c8c0a0c', }, }, - legacyResults: [legacyResults.beforeCatalystRegistrationCIP36], + legacyResults: [legacyResults.beforeGovernanceRegistrationCIP36], }, { @@ -1718,7 +1719,7 @@ export default { ], withdrawals: [SAMPLE_WITHDRAWALS.basic], auxiliaryData: { - catalystRegistrationParameters: { + governanceRegistrationParameters: { stakingPath: "m/1852'/1815'/0'/2/0", rewardAddressParameters: { addressType: CardanoAddressType.BASE, @@ -1726,7 +1727,7 @@ export default { stakingPath: "m/1852'/1815'/0'/2/0", }, nonce: '22634813', - format: CardanoCatalystRegistrationFormat.CIP36, + format: CardanoGovernanceRegistrationFormat.CIP36, delegations: [ { votingPublicKey: @@ -1777,11 +1778,11 @@ export default { type: 1, auxiliaryDataHash: '544c9ae849c82e31224865ff936decc6160047409eee4a6b4178b729fe3d286c', - catalystSignature: + governanceSignature: '3064949c9f186138f95e228075d0119dd5cb50e1b7e75d24d569fa547e018a597615da7c79a39ca8e394ee1ba8acb83e70be80f37e69aef3b86e7c4a6bd44903', }, }, - legacyResults: [legacyResults.beforeCatalystRegistrationCIP36], + legacyResults: [legacyResults.beforeGovernanceRegistrationCIP36], }, { diff --git a/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts b/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts index bb6a7e495af..c6bf5b4f460 100644 --- a/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts +++ b/packages/connect/src/api/cardano/cardanoAuxiliaryData.ts @@ -11,15 +11,15 @@ import type { Device } from '../../device/Device'; import { ERRORS, PROTO } from '../../constants'; import type { CardanoAuxiliaryData, - CardanoCatalystRegistrationDelegation, - CardanoCatalystRegistrationParameters, + CardanoGovernanceRegistrationDelegation, + CardanoGovernanceRegistrationParameters, } from '../../types/api/cardano'; const MAX_DELEGATION_COUNT = 32; const transformDelegation = ( - delegation: CardanoCatalystRegistrationDelegation, -): PROTO.CardanoCatalystRegistrationDelegation => { + delegation: CardanoGovernanceRegistrationDelegation, +): PROTO.CardanoGovernanceRegistrationDelegation => { validateParams(delegation, [ { name: 'votingPublicKey', type: 'string', required: true }, { name: 'weight', type: 'uint', required: true }, @@ -31,10 +31,10 @@ const transformDelegation = ( }; }; -const transformCatalystRegistrationParameters = ( - catalystRegistrationParameters: CardanoCatalystRegistrationParameters, -): PROTO.CardanoCatalystRegistrationParametersType => { - validateParams(catalystRegistrationParameters, [ +const transformGovernanceRegistrationParameters = ( + governanceRegistrationParameters: CardanoGovernanceRegistrationParameters, +): PROTO.CardanoGovernanceRegistrationParametersType => { + validateParams(governanceRegistrationParameters, [ { name: 'votingPublicKey', type: 'string' }, { name: 'stakingPath', required: true }, { name: 'nonce', type: 'uint', required: true }, @@ -42,26 +42,26 @@ const transformCatalystRegistrationParameters = ( { name: 'delegations', type: 'array', allowEmpty: true }, { name: 'votingPurpose', type: 'uint' }, ]); - validateAddressParameters(catalystRegistrationParameters.rewardAddressParameters); + validateAddressParameters(governanceRegistrationParameters.rewardAddressParameters); - const { delegations } = catalystRegistrationParameters; + const { delegations } = governanceRegistrationParameters; if (delegations && delegations.length > MAX_DELEGATION_COUNT) { throw ERRORS.TypedError( 'Method_InvalidParameter', - `At most ${MAX_DELEGATION_COUNT} delegations are allowed in a Catalyst registration`, + `At most ${MAX_DELEGATION_COUNT} delegations are allowed in a governance registration`, ); } return { - voting_public_key: catalystRegistrationParameters.votingPublicKey, - staking_path: validatePath(catalystRegistrationParameters.stakingPath, 3), + voting_public_key: governanceRegistrationParameters.votingPublicKey, + staking_path: validatePath(governanceRegistrationParameters.stakingPath, 3), reward_address_parameters: addressParametersToProto( - catalystRegistrationParameters.rewardAddressParameters, + governanceRegistrationParameters.rewardAddressParameters, ), - nonce: catalystRegistrationParameters.nonce, - format: catalystRegistrationParameters.format, + nonce: governanceRegistrationParameters.nonce, + format: governanceRegistrationParameters.format, delegations: delegations?.map(transformDelegation), - voting_purpose: catalystRegistrationParameters.votingPurpose, + voting_purpose: governanceRegistrationParameters.votingPurpose, }; }; @@ -75,16 +75,16 @@ export const transformAuxiliaryData = ( }, ]); - let catalystRegistrationParameters; - if (auxiliaryData.catalystRegistrationParameters) { - catalystRegistrationParameters = transformCatalystRegistrationParameters( - auxiliaryData.catalystRegistrationParameters, + let governanceRegistrationParameters; + if (auxiliaryData.governanceRegistrationParameters) { + governanceRegistrationParameters = transformGovernanceRegistrationParameters( + auxiliaryData.governanceRegistrationParameters, ); } return { hash: auxiliaryData.hash, - catalyst_registration_parameters: catalystRegistrationParameters, + governance_registration_parameters: governanceRegistrationParameters, }; }; @@ -92,17 +92,17 @@ export const modifyAuxiliaryDataForBackwardsCompatibility = ( device: Device, auxiliary_data: PROTO.CardanoTxAuxiliaryData, ): PROTO.CardanoTxAuxiliaryData => { - const { catalyst_registration_parameters } = auxiliary_data; - if (catalyst_registration_parameters) { - catalyst_registration_parameters.reward_address_parameters = + const { governance_registration_parameters } = auxiliary_data; + if (governance_registration_parameters) { + governance_registration_parameters.reward_address_parameters = modifyAddressParametersForBackwardsCompatibility( device, - catalyst_registration_parameters.reward_address_parameters, + governance_registration_parameters.reward_address_parameters, ); return { ...auxiliary_data, - catalyst_registration_parameters, + governance_registration_parameters, }; } diff --git a/packages/connect/src/api/cardanoSignTransaction.ts b/packages/connect/src/api/cardanoSignTransaction.ts index 33276ec58eb..6d99c0de677 100644 --- a/packages/connect/src/api/cardanoSignTransaction.ts +++ b/packages/connect/src/api/cardanoSignTransaction.ts @@ -44,7 +44,7 @@ const CardanoSignTransactionFeatures = Object.freeze({ Plutus: ['0', '2.4.4'], KeyHashStakeCredential: ['0', '2.4.4'], Babbage: ['0', '2.5.2'], - CatalystRegistrationCIP36: ['0', '2.5.3'], + GovernanceRegistrationCIP36: ['0', '2.5.3'], }); export type CardanoSignTransactionParams = { @@ -311,15 +311,15 @@ export default class CardanoSignTransaction extends AbstractMethod< this._ensureFeatureIsSupported('Babbage'); } - if (params.auxiliaryData?.catalyst_registration_parameters) { + if (params.auxiliaryData?.governance_registration_parameters) { const { format, delegations, voting_purpose } = - params.auxiliaryData.catalyst_registration_parameters; + params.auxiliaryData.governance_registration_parameters; if ( - format === PROTO.CardanoCatalystRegistrationFormat.CIP36 || + format === PROTO.CardanoGovernanceRegistrationFormat.CIP36 || delegations?.length || voting_purpose != null ) { - this._ensureFeatureIsSupported('CatalystRegistrationCIP36'); + this._ensureFeatureIsSupported('GovernanceRegistrationCIP36'); } } } @@ -381,8 +381,8 @@ export default class CardanoSignTransaction extends AbstractMethod< // auxiliary data let auxiliaryDataSupplement: CardanoAuxiliaryDataSupplement | undefined; if (this.params.auxiliaryData) { - const { catalyst_registration_parameters } = this.params.auxiliaryData; - if (catalyst_registration_parameters) { + const { governance_registration_parameters } = this.params.auxiliaryData; + if (governance_registration_parameters) { this.params.auxiliaryData = modifyAuxiliaryDataForBackwardsCompatibility( this.device, this.params.auxiliaryData, @@ -401,7 +401,7 @@ export default class CardanoSignTransaction extends AbstractMethod< auxiliaryDataSupplement = { type: auxiliaryDataType, auxiliaryDataHash: message.auxiliary_data_hash!, - catalystSignature: message.catalyst_signature, + governanceSignature: message.governance_signature, }; } await typedCall('CardanoTxHostAck', 'CardanoTxItemAck'); diff --git a/packages/connect/src/types/api/__tests__/cardano.ts b/packages/connect/src/types/api/__tests__/cardano.ts index 4cea8036497..b35ce9f08b2 100644 --- a/packages/connect/src/types/api/__tests__/cardano.ts +++ b/packages/connect/src/types/api/__tests__/cardano.ts @@ -2,7 +2,7 @@ import { TrezorConnect, PROTO } from '../../..'; const { CardanoAddressType, - CardanoCatalystRegistrationFormat, + CardanoGovernanceRegistrationFormat, CardanoCertificateType, CardanoNativeScriptHashDisplayFormat, CardanoNativeScriptType, @@ -322,7 +322,7 @@ export const cardanoSignTransaction = async (api: TrezorConnect) => { ], auxiliaryData: { hash: 'aaff00..', - catalystRegistrationParameters: { + governanceRegistrationParameters: { votingPublicKey: 'aaff00..', stakingPath: 'm/44', rewardAddressParameters: { @@ -337,7 +337,7 @@ export const cardanoSignTransaction = async (api: TrezorConnect) => { }, }, nonce: '0', - format: CardanoCatalystRegistrationFormat.CIP36, + format: CardanoGovernanceRegistrationFormat.CIP36, delegations: [ { votingPublicKey: 'aaff00..', @@ -413,10 +413,10 @@ export const cardanoSignTransaction = async (api: TrezorConnect) => { }); const { auxiliaryDataSupplement } = payload; if (auxiliaryDataSupplement) { - const { type, auxiliaryDataHash, catalystSignature } = auxiliaryDataSupplement; + const { type, auxiliaryDataHash, governanceSignature } = auxiliaryDataSupplement; type.toFixed(); auxiliaryDataHash.toLowerCase(); - catalystSignature?.toLowerCase(); + governanceSignature?.toLowerCase(); } } }; diff --git a/packages/connect/src/types/api/cardano/index.ts b/packages/connect/src/types/api/cardano/index.ts index 4d5328fda86..de51a4ffad2 100644 --- a/packages/connect/src/types/api/cardano/index.ts +++ b/packages/connect/src/types/api/cardano/index.ts @@ -173,24 +173,24 @@ export interface CardanoReferenceInput { prev_index: number; } -export interface CardanoCatalystRegistrationDelegation { +export interface CardanoGovernanceRegistrationDelegation { votingPublicKey: string; weight: number; } -export interface CardanoCatalystRegistrationParameters { +export interface CardanoGovernanceRegistrationParameters { votingPublicKey?: string; stakingPath: string | number[]; rewardAddressParameters: CardanoAddressParameters; nonce: string; - format?: PROTO.CardanoCatalystRegistrationFormat; - delegations?: CardanoCatalystRegistrationDelegation[]; + format?: PROTO.CardanoGovernanceRegistrationFormat; + delegations?: CardanoGovernanceRegistrationDelegation[]; votingPurpose?: number; } export interface CardanoAuxiliaryData { hash?: string; - catalystRegistrationParameters?: CardanoCatalystRegistrationParameters; + governanceRegistrationParameters?: CardanoGovernanceRegistrationParameters; } export interface CardanoSignTransaction { @@ -227,7 +227,7 @@ export interface CardanoSignedTxWitness { export interface CardanoAuxiliaryDataSupplement { type: PROTO.CardanoTxAuxiliaryDataSupplementType; auxiliaryDataHash: string; - catalystSignature?: string; + governanceSignature?: string; } export interface CardanoSignedTxData { diff --git a/packages/transport/messages.json b/packages/transport/messages.json index ebd4d2bf0a0..745d89b8211 100644 --- a/packages/transport/messages.json +++ b/packages/transport/messages.json @@ -1634,10 +1634,10 @@ "CardanoTxAuxiliaryDataSupplementType": { "values": { "NONE": 0, - "CATALYST_REGISTRATION_SIGNATURE": 1 + "GOVERNANCE_REGISTRATION_SIGNATURE": 1 } }, - "CardanoCatalystRegistrationFormat": { + "CardanoGovernanceRegistrationFormat": { "values": { "CIP15": 0, "CIP36": 1 @@ -2245,7 +2245,7 @@ } } }, - "CardanoCatalystRegistrationDelegation": { + "CardanoGovernanceRegistrationDelegation": { "fields": { "voting_public_key": { "rule": "required", @@ -2259,7 +2259,7 @@ } } }, - "CardanoCatalystRegistrationParametersType": { + "CardanoGovernanceRegistrationParametersType": { "fields": { "voting_public_key": { "type": "bytes", @@ -2284,7 +2284,7 @@ "id": 4 }, "format": { - "type": "CardanoCatalystRegistrationFormat", + "type": "CardanoGovernanceRegistrationFormat", "id": 5, "options": { "default": "CIP15" @@ -2292,7 +2292,7 @@ }, "delegations": { "rule": "repeated", - "type": "CardanoCatalystRegistrationDelegation", + "type": "CardanoGovernanceRegistrationDelegation", "id": 6 }, "voting_purpose": { @@ -2303,8 +2303,8 @@ }, "CardanoTxAuxiliaryData": { "fields": { - "catalyst_registration_parameters": { - "type": "CardanoCatalystRegistrationParametersType", + "governance_registration_parameters": { + "type": "CardanoGovernanceRegistrationParametersType", "id": 1 }, "hash": { @@ -2380,7 +2380,7 @@ "type": "bytes", "id": 2 }, - "catalyst_signature": { + "governance_signature": { "type": "bytes", "id": 3 } diff --git a/packages/transport/scripts/protobuf-patches/index.js b/packages/transport/scripts/protobuf-patches/index.js index b815ce466c8..16b7083f419 100644 --- a/packages/transport/scripts/protobuf-patches/index.js +++ b/packages/transport/scripts/protobuf-patches/index.js @@ -20,7 +20,7 @@ const RULE_PATCH = { 'CardanoNativeScript.scripts': 'optional', 'CardanoNativeScript.key_path': 'optional', 'CardanoTxRequiredSigner.key_path': 'optional', - 'CardanoCatalystRegistrationParametersType.delegations': 'optional', + 'CardanoGovernanceRegistrationParametersType.delegations': 'optional', 'Success.message': 'required', // didn't find use case where it's not sent 'SignedIdentity.address': 'required', 'EosAuthorizationKey.key': 'required', // its valid to be undefined according to implementation/tests @@ -112,9 +112,9 @@ const TYPE_PATCH = { 'Features.experimental_features': 'boolean | null', 'HDNodePathType.node': 'HDNodeType | string', 'FirmwareUpload.payload': 'Buffer | ArrayBuffer', - 'CardanoCatalystRegistrationDelegation.weight': UINT_TYPE, - 'CardanoCatalystRegistrationParametersType.nonce': UINT_TYPE, - 'CardanoCatalystRegistrationParametersType.voting_purpose': UINT_TYPE, + 'CardanoGovernanceRegistrationDelegation.weight': UINT_TYPE, + 'CardanoGovernanceRegistrationParametersType.nonce': UINT_TYPE, + 'CardanoGovernanceRegistrationParametersType.voting_purpose': UINT_TYPE, 'CardanoPoolParametersType.pledge': UINT_TYPE, 'CardanoPoolParametersType.cost': UINT_TYPE, 'CardanoPoolParametersType.margin_numerator': UINT_TYPE, diff --git a/packages/transport/src/types/messages.ts b/packages/transport/src/types/messages.ts index b9c6a725f21..56cbd30ba90 100644 --- a/packages/transport/src/types/messages.ts +++ b/packages/transport/src/types/messages.ts @@ -624,10 +624,10 @@ export enum CardanoPoolRelayType { export enum CardanoTxAuxiliaryDataSupplementType { NONE = 0, - CATALYST_REGISTRATION_SIGNATURE = 1, + GOVERNANCE_REGISTRATION_SIGNATURE = 1, } -export enum CardanoCatalystRegistrationFormat { +export enum CardanoGovernanceRegistrationFormat { CIP15 = 0, CIP36 = 1, } @@ -831,26 +831,26 @@ export type CardanoTxWithdrawal = { key_hash?: string; }; -// CardanoCatalystRegistrationDelegation -export type CardanoCatalystRegistrationDelegation = { +// CardanoGovernanceRegistrationDelegation +export type CardanoGovernanceRegistrationDelegation = { voting_public_key: string; weight: UintType; }; -// CardanoCatalystRegistrationParametersType -export type CardanoCatalystRegistrationParametersType = { +// CardanoGovernanceRegistrationParametersType +export type CardanoGovernanceRegistrationParametersType = { voting_public_key?: string; staking_path: number[]; reward_address_parameters: CardanoAddressParametersType; nonce: UintType; - format?: CardanoCatalystRegistrationFormat; - delegations?: CardanoCatalystRegistrationDelegation[]; + format?: CardanoGovernanceRegistrationFormat; + delegations?: CardanoGovernanceRegistrationDelegation[]; voting_purpose?: UintType; }; // CardanoTxAuxiliaryData export type CardanoTxAuxiliaryData = { - catalyst_registration_parameters?: CardanoCatalystRegistrationParametersType; + governance_registration_parameters?: CardanoGovernanceRegistrationParametersType; hash?: string; }; @@ -884,7 +884,7 @@ export type CardanoTxItemAck = {}; export type CardanoTxAuxiliaryDataSupplement = { type: CardanoTxAuxiliaryDataSupplementType; auxiliary_data_hash?: string; - catalyst_signature?: string; + governance_signature?: string; }; // CardanoTxWitnessRequest @@ -2255,8 +2255,8 @@ export type MessageType = { CardanoPoolParametersType: CardanoPoolParametersType; CardanoTxCertificate: CardanoTxCertificate; CardanoTxWithdrawal: CardanoTxWithdrawal; - CardanoCatalystRegistrationDelegation: CardanoCatalystRegistrationDelegation; - CardanoCatalystRegistrationParametersType: CardanoCatalystRegistrationParametersType; + CardanoGovernanceRegistrationDelegation: CardanoGovernanceRegistrationDelegation; + CardanoGovernanceRegistrationParametersType: CardanoGovernanceRegistrationParametersType; CardanoTxAuxiliaryData: CardanoTxAuxiliaryData; CardanoTxMint: CardanoTxMint; CardanoTxCollateralInput: CardanoTxCollateralInput; From 634ac8a3890630e08fed2626d3be5d6a81eb7fc9 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Wed, 28 Sep 2022 19:07:26 +0200 Subject: [PATCH 3/6] chore(cardano): add governance registration docs --- .../connect/methods/cardanoSignTransaction.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/packages/connect/methods/cardanoSignTransaction.md b/docs/packages/connect/methods/cardanoSignTransaction.md index 0fc4e23aeac..b0eb4062663 100644 --- a/docs/packages/connect/methods/cardanoSignTransaction.md +++ b/docs/packages/connect/methods/cardanoSignTransaction.md @@ -101,7 +101,16 @@ Trezor supports signing of stake pool registration certificates as a pool owner. 1. The transaction inputs must all be external, i.e. path must be either undefined or null 1. Exactly one owner should be passed as a staking path and the rest of owners should be passed as bech32-encoded reward addresses -### Example +### Governance registration (Catalyst and other) + +Trezor supports signing transactions with auxiliary data containing a governance registration. Governance registrations used to follow [CIP-15](https://cips.cardano.org/cips/cip15/), which has been superseded by [CIP-36](https://cips.cardano.org/cips/cip36/). Currently, Trezor supports both CIP-15 and CIP-36 formats, the intended standard can be specified in the `format` field (with CIP-15 being the default). They differ in the following: + +- CIP-36 allows delegating the voting power to several voting public keys with different voting power ([CardanoGovernanceRegistrationDelegation](https://github.com/trezor/trezor-suite/blob/develop/packages/connect/src/types/api/cardano/index.ts)) as an alternative to providing only a single voting public key. Note that Trezor Firmware supports at most 32 delegations in a single governance registration. +- CIP-36 registrations contain the [votingPurpose](https://github.com/trezor/trezor-suite/blob/develop/packages/connect/src/types/api/cardano/index.ts) field. The value 0 is intended for Catalyst voting and the value 1 is intended for other purposes. If no value is provided, Trezor serializes 0 by default (if the CIP-36 format is used). + +Trezor does not support the 1694 derivation paths at the moment. + +### Transaction examples #### Ordinary transaction From 4e12331878b99a030b709036ce9463ca468d0b7c Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 21 Oct 2022 16:16:19 +0200 Subject: [PATCH 4/6] fix(cardano): update requiredSigners version check and docs --- docs/packages/connect/methods/cardanoSignTransaction.md | 8 +++++--- packages/connect/src/api/cardanoSignTransaction.ts | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/packages/connect/methods/cardanoSignTransaction.md b/docs/packages/connect/methods/cardanoSignTransaction.md index b0eb4062663..8053045d4d7 100644 --- a/docs/packages/connect/methods/cardanoSignTransaction.md +++ b/docs/packages/connect/methods/cardanoSignTransaction.md @@ -46,7 +46,7 @@ The transaction - _should_ have valid `path` property on all `inputs` - _must not_ contain a pool registration certificate -- _must not_ contain `collateralInputs` and `requiredSigners` +- _must not_ contain `collateralInputs`, `collateralReturn`, `totalCollateral` and `referenceInputs` - _must_ contain paths as stake credentials in certificates and withdrawals (no key hashes or script hashes) - _may_ contain only 1852 and 1855 paths - _must not_ contain 1855 witness requests when transaction is not minting/burning tokens @@ -62,7 +62,7 @@ The transaction - _must_ have single owner given by path on that certificate - _must not_ contain withdrawals - _must not_ contain token minting -- _must not_ contain `collateralInputs` and `requiredSigners` +- _must not_ contain `collateralInputs`, `requiredSigners`, `collateralReturn`, `totalCollateral` and `referenceInputs` - _must_ contain only staking witness requests These restrictions are in place due to a possibility of maliciously signing _another_ part of the transaction with the pool owner path as we are not displaying device-owned paths on the device screen. @@ -76,7 +76,7 @@ The transaction - _must_ have `path` undefined on all `inputs` - _must not_ contain output addresses given by parameters - _must not_ contain a pool registration certificate -- _must not_ contain `collateralInputs` and `requiredSigners` +- _must not_ contain `collateralInputs`, `collateralReturn`, `totalCollateral` and `referenceInputs` - _must_ contain script hash stake credentials in certificates and withdrawals (no paths or key hashes) - _may_ contain only 1854 and 1855 witness requests - _must not_ contain 1855 witness requests when transaction is not minting/burning tokens @@ -92,6 +92,8 @@ The transaction - _may_ contain only 1852, 1854 and 1855 required signers - _may_ contain only 1852, 1854 and 1855 witness requests +Note: `requiredSigners` are meant for Plutus transactions (from the blockchain point of view), but some applications utilize them for their own purposes, so we allow them in all signing modes (except for pool registration as owner). + ### Stake pool registration certificate specifics Trezor supports signing of stake pool registration certificates as a pool owner. The transaction may contain external inputs (e.g. belonging to the pool operator) and Trezor is not able to verify whether they are actually external or not, so if we allowed signing the transaction with a spending key, there is the risk of losing funds from an input that the user did not intend to spend from. Moreover there is the risk of inadvertedly signing a withdrawal in the transaction if there's any. To mitigate those risks, we introduced special validation rules for stake pool registration transactions which are validated on Trezor as well. The validation rules are the following: diff --git a/packages/connect/src/api/cardanoSignTransaction.ts b/packages/connect/src/api/cardanoSignTransaction.ts index 6d99c0de677..05c401c4e48 100644 --- a/packages/connect/src/api/cardanoSignTransaction.ts +++ b/packages/connect/src/api/cardanoSignTransaction.ts @@ -311,6 +311,14 @@ export default class CardanoSignTransaction extends AbstractMethod< this._ensureFeatureIsSupported('Babbage'); } + if ( + params.requiredSigners.length > 0 && + params.signingMode !== PROTO.CardanoTxSigningMode.PLUTUS_TRANSACTION + ) { + // Trezor Firmware allowed requiredSigners in non-Plutus txs with the Babbage update + this._ensureFeatureIsSupported('Babbage'); + } + if (params.auxiliaryData?.governance_registration_parameters) { const { format, delegations, voting_purpose } = params.auxiliaryData.governance_registration_parameters; From 6c2e34e48578da56fb17d9d9e178d7b7e1baa0b2 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 1 Nov 2022 11:52:10 +0100 Subject: [PATCH 5/6] feat(cardano): add preview and preprod testnets --- .../connect/methods/cardanoGetAddress.md | 4 ++-- .../connect/methods/cardanoSignTransaction.md | 2 +- .../e2e/__fixtures__/cardanoGetAddress.ts | 22 +++++++++---------- packages/connect/src/constants/cardano.ts | 5 ++++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/packages/connect/methods/cardanoGetAddress.md b/docs/packages/connect/methods/cardanoGetAddress.md index 987b279cf02..c56ae917bea 100644 --- a/docs/packages/connect/methods/cardanoGetAddress.md +++ b/docs/packages/connect/methods/cardanoGetAddress.md @@ -16,7 +16,7 @@ const result = await TrezorConnect.cardanoGetAddress(params); - `addressParameters` — _required_ see description below - `address` — _optional_ `string` address for validation (read `Handle button request` section below) -- `protocolMagic` - _required_ `Integer` 764824073 for Mainnet, 1097911063 for Testnet +- `protocolMagic` - _required_ `Integer` 764824073 for Mainnet, 1 for Preprod Testnet, 2 for Preview Testnet - `networkId` - _required_ `Integer` 1 for Mainnet, 0 for Testnet - `showOnTrezor` — _optional_ `boolean` determines if address will be displayed on device. Default is set to `true` - `derivationType` — _optional_ `CardanoDerivationType` enum. determines used derivation type. Default is set to ICARUS_TREZOR=2 @@ -242,7 +242,7 @@ TrezorConnect.cardanoGetAddress({ addressType: 8, path: "m/44'/1815'/1'/0/2", }, - protocolMagic: 1097911063, + protocolMagic: 1, networkId: 0, showOnTrezor: false, }, diff --git a/docs/packages/connect/methods/cardanoSignTransaction.md b/docs/packages/connect/methods/cardanoSignTransaction.md index 8053045d4d7..507c4e56055 100644 --- a/docs/packages/connect/methods/cardanoSignTransaction.md +++ b/docs/packages/connect/methods/cardanoSignTransaction.md @@ -17,7 +17,7 @@ const result = await TrezorConnect.cardanoSignTransaction(params); - `inputs` - _required_ `Array` of [CardanoInput](https://github.com/trezor/trezor-suite/blob/develop/packages/connect/src/types/api/cardano/index.ts) - `outputs` - _required_ `Array` of [CardanoOutput](https://github.com/trezor/trezor-suite/blob/develop/packages/connect/src/types/api/cardano/index.ts) - `fee` - _required_ `String` -- `protocolMagic` - _required_ `Integer` 764824073 for Mainnet, 1097911063 for Testnet +- `protocolMagic` - _required_ `Integer` 764824073 for Mainnet, 1 for Preprod Testnet, 2 for Preview Testnet - `networkId` - _required_ `Integer` 1 for Mainnet, 0 for Testnet - `ttl` - _optional_ `String` - `validityIntervalStart` - _optional_ `String` diff --git a/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts b/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts index 4d1b482a086..2b25cfebde2 100644 --- a/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts +++ b/packages/connect/e2e/__fixtures__/cardanoGetAddress.ts @@ -159,7 +159,7 @@ export default { path: "m/1852'/1815'/4'/0/0", stakingPath: "m/1852'/1815'/4'/2/0", }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -193,7 +193,7 @@ export default { path: "m/1852'/1815'/4'/0/0", stakingKeyHash: '1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff', }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -232,7 +232,7 @@ export default { paymentScriptHash: '0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe', stakingPath: "m/1852'/1815'/0'/2/0", }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -276,7 +276,7 @@ export default { path: "m/1852'/1815'/0'/0/0", stakingScriptHash: '8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9', }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -320,7 +320,7 @@ export default { paymentScriptHash: '0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe', stakingScriptHash: '8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9', }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -356,7 +356,7 @@ export default { addressType: CardanoAddressType.ENTERPRISE, path: "m/1852'/1815'/0'/0/0", }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -391,7 +391,7 @@ export default { addressType: CardanoAddressType.ENTERPRISE_SCRIPT, paymentScriptHash: '0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe', }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -436,7 +436,7 @@ export default { certificateIndex: 42, }, }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -481,7 +481,7 @@ export default { certificateIndex: 42, }, }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -516,7 +516,7 @@ export default { addressType: CardanoAddressType.REWARD, stakingPath: "m/1852'/1815'/0'/2/0", }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { @@ -551,7 +551,7 @@ export default { addressType: CardanoAddressType.REWARD_SCRIPT, stakingScriptHash: '8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9', }, - protocolMagic: PROTOCOL_MAGICS.testnet, + protocolMagic: PROTOCOL_MAGICS.testnet_preprod, networkId: NETWORK_IDS.testnet, }, result: { diff --git a/packages/connect/src/constants/cardano.ts b/packages/connect/src/constants/cardano.ts index d0c79cc72aa..db8f34f9558 100644 --- a/packages/connect/src/constants/cardano.ts +++ b/packages/connect/src/constants/cardano.ts @@ -1,6 +1,9 @@ +// https://book.world.dev.cardano.org/environments.html export enum PROTOCOL_MAGICS { mainnet = 764824073, - testnet = 1097911063, + testnet_preprod = 1, + testnet_preview = 2, + testnet_legacy = 1097911063, } export enum NETWORK_IDS { From a6c884e8254756f37eaff3fbb395d2ebccaa7a16 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 10 Nov 2022 12:56:02 +0100 Subject: [PATCH 6/6] feat(cardano): keep catalyst registration backward compatibility --- packages/connect/src/api/cardanoSignTransaction.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/connect/src/api/cardanoSignTransaction.ts b/packages/connect/src/api/cardanoSignTransaction.ts index 05c401c4e48..e48a05a07a6 100644 --- a/packages/connect/src/api/cardanoSignTransaction.ts +++ b/packages/connect/src/api/cardanoSignTransaction.ts @@ -103,6 +103,17 @@ export default class CardanoSignTransaction extends AbstractMethod< ); } + // @ts-expect-error payload.auxiliaryData.catalystRegistrationParameters is a legacy param + // kept for backward compatibility (for now) + if (payload.auxiliaryData && payload.auxiliaryData.catalystRegistrationParameters) { + console.warn( + 'Please use governanceRegistrationParameters instead of catalystRegistrationParameters.', + ); + payload.auxiliaryData.governanceRegistrationParameters = + // @ts-expect-error + payload.auxiliaryData.catalystRegistrationParameters; + } + // validate incoming parameters validateParams(payload, [ { name: 'signingMode', type: 'number', required: true }, @@ -410,6 +421,8 @@ export default class CardanoSignTransaction extends AbstractMethod< type: auxiliaryDataType, auxiliaryDataHash: message.auxiliary_data_hash!, governanceSignature: message.governance_signature, + // @ts-expect-error auxiliaryDataSupplement.catalystSignature kept for backward compatibility + catalystSignature: message.governance_signature, }; } await typedCall('CardanoTxHostAck', 'CardanoTxItemAck');