Skip to content

Commit

Permalink
feat: ada support conway
Browse files Browse the repository at this point in the history
  • Loading branch information
ByteZhang1024 committed Oct 15, 2024
1 parent 3b1929d commit f800200
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 72 deletions.
82 changes: 80 additions & 2 deletions packages/core/src/api/cardano/CardanoSignTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import semver from 'semver';
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
import { BaseMethod } from '../BaseMethod';
import { PROTO } from '../../constants';
import { UI_REQUEST } from '../../constants/ui-request';
Expand All @@ -24,7 +26,10 @@ import type {
CardanoSignedTxData,
CardanoSignedTxWitness,
CardanoAuxiliaryDataSupplement,
CardanoSignTransaction as CardanoSignTransactionType,
} from '../../types/api/cardano';
import { DeviceFirmwareRange } from '../../types';
import { getDeviceFirmwareVersion, getMethodVersionRange } from '../../utils';

export default class CardanoSignTransaction extends BaseMethod<any> {
hasBundle?: boolean;
Expand All @@ -48,6 +53,16 @@ export default class CardanoSignTransaction extends BaseMethod<any> {

const { payload } = this;

// convert legacy parameters to new parameter
// payload.auxiliaryData.governanceRegistrationParameters are legacy params kept for backward compatibility (for now)
if (payload.auxiliaryData && payload.auxiliaryData.governanceRegistrationParameters) {
console.warn(
'Please use cVoteRegistrationParameters instead of governanceRegistrationParameters.'
);
payload.auxiliaryData.cVoteRegistrationParameters =
payload.auxiliaryData.governanceRegistrationParameters;
}

// validate incoming parameters
validateParams(payload, [
{ name: 'signingMode', type: 'number', required: true },
Expand All @@ -69,6 +84,8 @@ export default class CardanoSignTransaction extends BaseMethod<any> {
{ name: 'additionalWitnessRequests', type: 'array', allowEmpty: true },
{ name: 'derivationType', type: 'number' },
{ name: 'includeNetworkId', type: 'boolean' },
{ name: 'chunkify', type: 'boolean' },
{ name: 'tagCborSets', type: 'boolean' },
]);

const inputsWithPath = payload.inputs.map(transformInput);
Expand Down Expand Up @@ -172,9 +189,65 @@ export default class CardanoSignTransaction extends BaseMethod<any> {
? payload.derivationType
: PROTO.CardanoDerivationType.ICARUS,
includeNetworkId: payload.includeNetworkId,
chunkify: payload.chunkify,
tagCborSets: payload.tagCborSets,
};
}

hasConway = () => {
const payload = this.payload as CardanoSignTransactionType;
if (payload.tagCborSets != null) {
return true;
}
if (payload.auxiliaryData?.cVoteRegistrationParameters != null) {
return true;
}
for (const certificate of payload.certificates ?? []) {
if (certificate.dRep != null) {
return true;
}
if (certificate.deposit != null) {
return true;
}
if (
certificate.type === PROTO.CardanoCertificateType.STAKE_REGISTRATION_CONWAY ||
certificate.type === PROTO.CardanoCertificateType.STAKE_DEREGISTRATION_CONWAY ||
certificate.type === PROTO.CardanoCertificateType.VOTE_DELEGATION
) {
return true;
}
}

return false;
};

supportConwayVersionRange = (): DeviceFirmwareRange => ({
model_touch: {
min: '4.11.0',
},
});

checkSupportConway = () => {
const firmwareVersion = getDeviceFirmwareVersion(this.device.features)?.join('.');

const versionRange = getMethodVersionRange(
this.device.features,
type => this.supportConwayVersionRange()[type]
);

if (!versionRange) {
return;
}

if (!semver.valid(firmwareVersion) || semver.lt(firmwareVersion, versionRange.min)) {
throw ERRORS.TypedError(
HardwareErrorCode.CallMethodNeedUpgradeFirmware,
`Device firmware version is too low, please update to ${versionRange.min}`,
{ current: firmwareVersion, require: versionRange.min }
);
}
};

async signTx(): Promise<CardanoSignedTxData> {
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());

Expand Down Expand Up @@ -202,6 +275,8 @@ export default class CardanoSignTransaction extends BaseMethod<any> {
reference_inputs_count: this.params.referenceInputs.length,
derivation_type: this.params.derivationType,
include_network_id: this.params.includeNetworkId,
chunkify: this.params.chunkify,
tag_cbor_sets: this.params.tagCborSets,
};

// init
Expand Down Expand Up @@ -250,9 +325,10 @@ export default class CardanoSignTransaction extends BaseMethod<any> {
auxiliaryDataSupplement = {
type: auxiliaryDataType,
auxiliaryDataHash: message.auxiliary_data_hash as unknown as string,
cVoteRegistrationSignature: message.cvote_registration_signature,
// @ts-expect-error
governanceSignature: message.governance_signature,
catalystSignature: message.governance_signature,
catalystSignature: message.cvote_registration_signature,
governanceSignature: message.cvote_registration_signature,
};
}
await typedCall('CardanoTxHostAck', 'CardanoTxItemAck');
Expand Down Expand Up @@ -310,6 +386,8 @@ export default class CardanoSignTransaction extends BaseMethod<any> {
}

run() {
this.checkSupportConway();

return this.signTx();
}
}
87 changes: 56 additions & 31 deletions packages/core/src/api/cardano/helper/auxiliaryData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,65 @@ import { validateParams } from '../../helpers/paramsValidator';
import { validatePath } from '../../helpers/pathUtils';
import type {
CardanoAuxiliaryData,
CardanoGovernanceRegistrationDelegation,
CardanoGovernanceRegistrationParameters,
CardanoCVoteRegistrationParameters,
CardanoCVoteRegistrationDelegation,
} from '../../../types/api/cardano';
import { PROTO } from '../../../constants';

const MAX_DELEGATION_COUNT = 32;

const transformDelegation = (
delegation: CardanoGovernanceRegistrationDelegation
): PROTO.CardanoGovernanceRegistrationDelegation => {
delegation: CardanoCVoteRegistrationDelegation
): PROTO.CardanoCVoteRegistrationDelegation => {
// @ts-expect-error votingPublicKey is a legacy param kept for backward compatibility (for now)
if (delegation.votingPublicKey) {
console.warn('Please use votePublicKey instead of votingPublicKey.');
// @ts-expect-error
delegation.votePublicKey = delegation.votingPublicKey;
}

validateParams(delegation, [
{ name: 'votingPublicKey', type: 'string', required: true },
{ name: 'weight', type: 'uint', required: true },
]);

return {
voting_public_key: delegation.votingPublicKey,
vote_public_key: delegation.votePublicKey,
weight: delegation.weight,
};
};

const transformGovernanceRegistrationParameters = (
governanceRegistrationParameters: CardanoGovernanceRegistrationParameters
): PROTO.CardanoGovernanceRegistrationParametersType => {
validateParams(governanceRegistrationParameters, [
{ name: 'votingPublicKey', type: 'string' },
const transformCvoteRegistrationParameters = (
cVoteRegistrationParameters: CardanoCVoteRegistrationParameters
): PROTO.CardanoCVoteRegistrationParametersType => {
// votingPublicKey and rewardAddressParameters are legacy params kept for backward compatibility (for now)
// @ts-expect-error
if (cVoteRegistrationParameters.votingPublicKey) {
console.warn('Please use votePublicKey instead of votingPublicKey.');
// @ts-expect-error
cVoteRegistrationParameters.votePublicKey = cVoteRegistrationParameters.votingPublicKey;
}
// @ts-expect-error
if (cVoteRegistrationParameters.rewardAddressParameters) {
console.warn('Please use paymentAddressParameters instead of rewardAddressParameters.');
cVoteRegistrationParameters.paymentAddressParameters =
// @ts-expect-error
cVoteRegistrationParameters.rewardAddressParameters;
}

validateParams(cVoteRegistrationParameters, [
{ name: 'votePublicKey', 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' },
{ name: 'paymentAddress', type: 'string' },
]);
validateAddressParameters(governanceRegistrationParameters.rewardAddressParameters);
const { paymentAddressParameters } = cVoteRegistrationParameters;
validateAddressParameters(paymentAddressParameters);

const { delegations } = governanceRegistrationParameters;
const { delegations } = cVoteRegistrationParameters;
if (delegations && delegations.length > MAX_DELEGATION_COUNT) {
throw ERRORS.TypedError(
HardwareErrorCode.CallMethodInvalidParameter,
Expand All @@ -51,15 +75,16 @@ const transformGovernanceRegistrationParameters = (
}

return {
voting_public_key: governanceRegistrationParameters.votingPublicKey,
staking_path: validatePath(governanceRegistrationParameters.stakingPath, 3),
reward_address_parameters: addressParametersToProto(
governanceRegistrationParameters.rewardAddressParameters
),
nonce: governanceRegistrationParameters.nonce as unknown as number,
format: governanceRegistrationParameters.format,
delegations: delegations?.map(transformDelegation) as any,
voting_purpose: governanceRegistrationParameters.votingPurpose,
vote_public_key: cVoteRegistrationParameters.votePublicKey,
staking_path: validatePath(cVoteRegistrationParameters.stakingPath, 3),
payment_address_parameters: paymentAddressParameters
? addressParametersToProto(paymentAddressParameters)
: undefined,
nonce: cVoteRegistrationParameters.nonce as unknown as number,
format: cVoteRegistrationParameters.format,
delegations: delegations?.map(transformDelegation) ?? [],
voting_purpose: cVoteRegistrationParameters.votingPurpose,
payment_address: cVoteRegistrationParameters.paymentAddress,
};
};

Expand All @@ -73,32 +98,32 @@ export const transformAuxiliaryData = (
},
]);

let governanceRegistrationParameters;
if (auxiliaryData.governanceRegistrationParameters) {
governanceRegistrationParameters = transformGovernanceRegistrationParameters(
auxiliaryData.governanceRegistrationParameters
let cVoteRegistrationParameters;
if (auxiliaryData.cVoteRegistrationParameters) {
cVoteRegistrationParameters = transformCvoteRegistrationParameters(
auxiliaryData.cVoteRegistrationParameters
);
}

return {
hash: auxiliaryData.hash,
governance_registration_parameters: governanceRegistrationParameters,
cvote_registration_parameters: cVoteRegistrationParameters,
};
};

export const modifyAuxiliaryDataForBackwardsCompatibility = (
auxiliary_data: PROTO.CardanoTxAuxiliaryData
): PROTO.CardanoTxAuxiliaryData => {
const { governance_registration_parameters } = auxiliary_data;
if (governance_registration_parameters) {
governance_registration_parameters.reward_address_parameters =
const { cvote_registration_parameters } = auxiliary_data;
if (cvote_registration_parameters?.payment_address_parameters) {
cvote_registration_parameters.payment_address_parameters =
modifyAddressParametersForBackwardsCompatibility(
governance_registration_parameters.reward_address_parameters
cvote_registration_parameters.payment_address_parameters
);

return {
...auxiliary_data,
governance_registration_parameters,
cvote_registration_parameters,
};
}

Expand Down
47 changes: 47 additions & 0 deletions packages/core/src/api/cardano/helper/certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
CardanoPoolMetadata,
CardanoPoolRelay,
CardanoPoolOwner,
CardanoDRep,
} from '../../../types/api/cardano';
import { PROTO } from '../../../constants';

Expand Down Expand Up @@ -156,6 +157,37 @@ const transformPoolParameters = (
};
};

const transformDRep = (dRep: CardanoDRep | undefined): PROTO.CardanoDRep | undefined => {
if (!dRep) {
return undefined;
}

validateParams(dRep, [
{ name: 'type', type: 'number', required: true },
{ name: 'keyHash', type: 'string' },
{ name: 'scriptHash', type: 'string' },
]);

if (dRep.type === PROTO.CardanoDRepType.KEY_HASH && !dRep.keyHash) {
throw ERRORS.TypedError(
HardwareErrorCode.CallMethodInvalidParameter,
'key_hash must be supplied for key_hash type'
);
}

if (dRep.type === PROTO.CardanoDRepType.SCRIPT_HASH && !dRep.scriptHash) {
throw ERRORS.TypedError(
HardwareErrorCode.CallMethodInvalidParameter,
'script_hash must be supplied for script_hash type'
);
}
return {
type: dRep.type,
key_hash: dRep.keyHash,
script_hash: dRep.scriptHash,
};
};

export const transformCertificate = (
certificate: CardanoCertificate
): CertificateWithPoolOwnersAndRelays => {
Expand All @@ -176,12 +208,25 @@ export const transformCertificate = (
paramsToValidate.push({ name: 'poolParameters', type: 'object', required: true });
}

if (
certificate.type === PROTO.CardanoCertificateType.STAKE_REGISTRATION_CONWAY ||
certificate.type === PROTO.CardanoCertificateType.STAKE_DEREGISTRATION_CONWAY
) {
paramsToValidate.push({ name: 'deposit', required: true });
}

if (certificate.type === PROTO.CardanoCertificateType.VOTE_DELEGATION) {
paramsToValidate.push({ name: 'dRep', type: 'object', required: true });
}

validateParams(certificate, paramsToValidate);

const { poolParameters, poolOwners, poolRelays } = transformPoolParameters(
certificate.poolParameters
);

const dRep = transformDRep(certificate.dRep);

return {
certificate: {
type: certificate.type,
Expand All @@ -190,6 +235,8 @@ export const transformCertificate = (
key_hash: certificate.keyHash,
pool: certificate.pool,
pool_parameters: poolParameters,
deposit: certificate.deposit,
drep: dRep,
},
poolOwners,
poolRelays,
Expand Down
Loading

0 comments on commit f800200

Please sign in to comment.