Skip to content

Commit

Permalink
feat(cardano): add support for catalyst voting registration
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielKerekes committed Apr 7, 2021
1 parent 4cd54eb commit 5631472
Show file tree
Hide file tree
Showing 11 changed files with 564 additions and 30 deletions.
61 changes: 50 additions & 11 deletions docs/methods/cardanoSignTransaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,20 @@ TrezorConnect.cardanoSignTransaction(params).then(function(result) {
});
```

### Notes
**Unfortunately we are aware of the fact that currently at most ~14 inputs are supported per transaction. This should be resolved when the cardano app is updated to support transaction streaming. Meanwhile, a workaround is to send multiple smaller transactions containing less inputs.**

**Also, each serialized transaction output size is currently limited to 512 bytes at Trezor firmware level. This limitation is a mitigation measure to prevent sending large (especially change) outputs containing many tokens that Trezor would not be able to spend given that currently the full Cardano transaction is held in-memory. Once Cardano-transaction signing is refactored to be streamed, this limit can be lifted.**

### Params
[****Optional common params****](commonParams.md)
###### [flowtype](../../src/js/types/networks/cardano.js#L62-L109)
###### [flowtype](../../src/js/types/networks/cardano.js#L61-L171)
* `inputs`*obligatory* `Array` of [CardanoInput](../../src/js/types/networks/cardano.js#L61)
* `outputs` - *obligatory* `Array` of [CardanoOutput](../../src/js/types/networks/cardano.js#L76)
* `fee` - *obligatory* `String`
* `protocolMagic` - *obligatory* `Integer` 764824073 for Mainnet, 42 for Testnet
* `networkId` - *obligatory* `Integer` 1 for Mainnet, 0 for Testnet
* `ttl` - *optional* `String`
* `validityIntervalStart` - *optional* `String`
* `certificates` - *optional* `Array` of [CardanoCertificate](../../src/js/types/networks/cardano.js#L85)
* `withdrawals` - *optional* `Array` of [CardanoWithdrawal](../../src/js/types/networks/cardano.js#L90)
* `metadata` - *optional* `String`
* `certificates` - *optional* `Array` of [CardanoCertificate](../../src/js/types/networks/cardano.js#L123)
* `withdrawals` - *optional* `Array` of [CardanoWithdrawal](../../src/js/types/networks/cardano.js#L130)
* `auixiliaryData` - *optional* `[CardanoAuxiliaryData]`(../../src/js/types/networks/cardano.js#147)
* `metadata` - *removed* - use `auxiliaryData` instead

### Stake pool registration certificate specifics

Expand Down Expand Up @@ -109,7 +105,10 @@ TrezorConnect.cardanoSignTransaction({
amount: "1000",
}
],
metadata: "a200a11864a118c843aa00ff01a119012c590100aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
auxiliaryData: {
type: 0,
blob: "a200a11864a118c843aa00ff01a119012c590100aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
protocolMagic: 764824073,
networkId: 1,
});
Expand Down Expand Up @@ -191,8 +190,48 @@ TrezorConnect.cardanoSignTransaction({
});
```

#### Catalyst voting key registration
```javascript
TrezorConnect.cardanoSignTransaction({
inputs: [
{
path: "m/1852'/1815'/0'/0/0",
prev_hash: "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
prev_index: 0,
}
],
outputs: [
{
address: "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
amount: "3003112",
}
],
fee: "42",
ttl: "10",
auxiliaryData: {
type: 1,
metadata: {
type: 0,
catalystRegistrationParameters: {
votingPublicKey:
"1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
stakingPath: "m/1852'/1815'/0'/2/0",
rewardAddressParameters: {
addressType: 0,
path: "m/1852'/1815'/0'/0/0",
stakingPath: "m/1852'/1815'/0'/2/0",
},
nonce: 22634813,
},
},
},
protocolMagic: 764824073,
networkId: 1,
});
```

### Result
###### [flowtype](../../src/js/types/networks/cardano.js#L107-L110)
###### [flowtype](../../src/js/types/networks/cardano.js#L167-L171)
```javascript
{
success: true,
Expand Down
126 changes: 120 additions & 6 deletions src/data/messages/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2358,16 +2358,16 @@
{
"rule": "optional",
"options": {},
"type": "bytes",
"name": "metadata",
"id": 11
"type": "uint64",
"name": "validity_interval_start",
"id": 12
},
{
"rule": "optional",
"options": {},
"type": "uint64",
"name": "validity_interval_start",
"id": 12
"type": "CardanoTxAuxiliaryDataType",
"name": "auxiliary_data",
"id": 13
}
],
"enums": [],
Expand Down Expand Up @@ -2713,6 +2713,96 @@
"messages": [],
"options": {},
"oneofs": {}
},
{
"name": "CardanoCatalystRegistrationParametersType",
"fields": [
{
"rule": "required",
"options": {},
"type": "bytes",
"name": "voting_public_key",
"id": 1
},
{
"rule": "repeated",
"options": {},
"type": "uint32",
"name": "staking_path",
"id": 2
},
{
"rule": "required",
"options": {},
"type": "CardanoAddressParametersType",
"name": "reward_address_parameters",
"id": 3
},
{
"rule": "required",
"options": {},
"type": "uint64",
"name": "nonce",
"id": 4
}
],
"enums": [],
"messages": [],
"options": {},
"oneofs": {}
},
{
"name": "CardanoTxMetadataType",
"fields": [
{
"rule": "required",
"options": {},
"type": "CardanoMetadataType",
"name": "type",
"id": 1
},
{
"rule": "optional",
"options": {},
"type": "CardanoCatalystRegistrationParametersType",
"name": "catalyst_registration_parameters",
"id": 2
}
],
"enums": [],
"messages": [],
"options": {},
"oneofs": {}
},
{
"name": "CardanoTxAuxiliaryDataType",
"fields": [
{
"rule": "required",
"options": {},
"type": "CardanoAuxiliaryDataType",
"name": "type",
"id": 1
},
{
"rule": "optional",
"options": {},
"type": "bytes",
"name": "blob",
"id": 2
},
{
"rule": "optional",
"options": {},
"type": "CardanoTxMetadataType",
"name": "metadata",
"id": 3
}
],
"enums": [],
"messages": [],
"options": {},
"oneofs": {}
}
],
"options": {},
Expand Down Expand Up @@ -10674,6 +10764,30 @@
],
"options": {}
},
{
"name": "CardanoAuxiliaryDataType",
"values": [
{
"name": "BLOB",
"id": 0
},
{
"name": "TUPLE",
"id": 1
}
],
"options": {}
},
{
"name": "CardanoMetadataType",
"values": [
{
"name": "CATALYST_REGISTRATION",
"id": 0
}
],
"options": {}
},
{
"name": "BackupType",
"values": [
Expand Down
9 changes: 9 additions & 0 deletions src/js/constants/cardano.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,12 @@ export const POOL_RELAY_TYPE = Object.freeze({
SingleHostName: 1,
MultipleHostName: 2,
});

export const AUXILIARY_DATA_TYPE = Object.freeze({
Blob: 0,
Tuple: 1,
});

export const METADATA_TYPE = Object.freeze({
CatalystRegistration: 0,
});
14 changes: 12 additions & 2 deletions src/js/core/methods/CardanoSignTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
addressParametersToProto,
validateAddressParameters,
} from './helpers/cardanoAddressParameters';
import { transformAuxiliaryData } from './helpers/cardanoAuxiliaryData';
import { transformCertificate } from './helpers/cardanoCertificate';
import { validateTokenBundle, tokenBundleToProto } from './helpers/cardanoTokens';
import { ERRORS } from '../../constants';
Expand All @@ -27,6 +28,7 @@ const CardanoSignTransactionFeatures = Object.freeze({
SignStakePoolRegistrationAsOwner: ['0', '2.3.5'],
ValidityIntervalStart: ['0', '2.3.5'],
MultiassetOutputs: ['0', '2.3.5'],
AuxiliaryDataAndCatalystRegistration: ['0', '2.3.7'],
});

export default class CardanoSignTransaction extends AbstractMethod {
Expand All @@ -51,7 +53,6 @@ export default class CardanoSignTransaction extends AbstractMethod {
{ name: 'ttl', type: 'amount' },
{ name: 'certificates', type: 'array', allowEmpty: true },
{ name: 'withdrawals', type: 'array', allowEmpty: true },
{ name: 'metadata', type: 'string' },
{ name: 'validityIntervalStart', type: 'amount' },
{ name: 'protocolMagic', type: 'number', obligatory: true },
{ name: 'networkId', type: 'number', obligatory: true },
Expand Down Expand Up @@ -116,17 +117,22 @@ export default class CardanoSignTransaction extends AbstractMethod {
});
}

let auxiliaryData;
if (payload.auxiliaryData) {
auxiliaryData = transformAuxiliaryData(payload.auxiliaryData);
}

this.params = {
inputs,
outputs,
fee: payload.fee,
ttl: payload.ttl,
certificates,
withdrawals,
metadata: payload.metadata,
validity_interval_start: payload.validityIntervalStart,
protocol_magic: payload.protocolMagic,
network_id: payload.networkId,
auxiliary_data: auxiliaryData,
};
}

Expand Down Expand Up @@ -157,6 +163,10 @@ export default class CardanoSignTransaction extends AbstractMethod {
this._ensureFeatureIsSupported('MultiassetOutputs');
}
});

if (params.auxiliary_data) {
this._ensureFeatureIsSupported('AuxiliaryDataAndCatalystRegistration');
}
}

async run() {
Expand Down
78 changes: 78 additions & 0 deletions src/js/core/methods/helpers/cardanoAuxiliaryData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* @flow */
import { addressParametersToProto, validateAddressParameters } from './cardanoAddressParameters';
import { validateParams } from './paramsValidator';
import { validatePath } from '../../../utils/pathUtils';

import type {
CardanoTxAuxiliaryDataType,
CardanoTxMetadataType,
CardanoCatalystRegistrationParametersType,
} from '../../../types/trezor/protobuf';
import type {
CardanoAuxiliaryData,
CardanoMetadata,
CardanoCatalystRegistrationParameters,
} from '../../../types/networks/cardano';

const transformCatalystRegistrationParameters = (
catalystRegistrationParameters: CardanoCatalystRegistrationParameters,
): CardanoCatalystRegistrationParametersType => {
validateParams(catalystRegistrationParameters, [
{ name: 'votingPublicKey', type: 'string', obligatory: true },
{ name: 'stakingPath', obligatory: true },
{ name: 'nonce', type: 'number', obligatory: true },
]);
validateAddressParameters(catalystRegistrationParameters.rewardAddressParameters);

return {
voting_public_key: catalystRegistrationParameters.votingPublicKey,
staking_path: validatePath(catalystRegistrationParameters.stakingPath, 3),
reward_address_parameters: addressParametersToProto(
catalystRegistrationParameters.rewardAddressParameters,
),
nonce: catalystRegistrationParameters.nonce,
};
};

const transformMetadata = (metadata: CardanoMetadata): CardanoTxMetadataType => {
validateParams(metadata, [{ name: 'type', type: 'number', obligatory: true }]);

let catalystRegistrationParameters;
if (metadata.catalystRegistrationParameters) {
catalystRegistrationParameters = transformCatalystRegistrationParameters(
metadata.catalystRegistrationParameters,
);
}

return {
type: metadata.type,
catalyst_registration_parameters: catalystRegistrationParameters,
};
};

export const transformAuxiliaryData = (
auxiliaryData: CardanoAuxiliaryData,
): CardanoTxAuxiliaryDataType => {
validateParams(auxiliaryData, [
{
name: 'type',
type: 'number',
obligatory: true,
},
{
name: 'blob',
type: 'string',
},
]);

let metadata;
if (auxiliaryData.metadata) {
metadata = transformMetadata(auxiliaryData.metadata);
}

return {
type: auxiliaryData.type,
blob: auxiliaryData.blob,
metadata,
};
};
Loading

0 comments on commit 5631472

Please sign in to comment.