From 76ab44c859c64877860c500cc67df1c296b7265d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomislav=20Hora=C4=8Dek?= Date: Thu, 8 Feb 2024 12:16:43 +0100 Subject: [PATCH] chore(hardware-trezor): refactor TrezorKeyAgent to use Transform interface --- .../hardware-trezor/src/TrezorKeyAgent.ts | 9 +- .../transformers/additionalWitnessRequests.ts | 21 ++-- .../src/transformers/assets.ts | 3 +- .../src/transformers/auxiliaryData.ts | 6 +- .../src/transformers/certificates.ts | 118 ++++++++---------- .../src/transformers/keyPaths.ts | 10 +- .../src/transformers/requiredSigners.ts | 33 +++-- .../hardware-trezor/src/transformers/tx.ts | 70 +++++------ .../hardware-trezor/src/transformers/txOut.ts | 37 +++--- .../src/transformers/withdrawals.ts | 27 ++-- packages/hardware-trezor/test/testData.ts | 2 +- .../additionalWitnessRequests.test.ts | 4 +- .../test/transformers/keyPaths.test.ts | 4 +- .../test/transformers/tx.test.ts | 49 ++------ 14 files changed, 185 insertions(+), 208 deletions(-) diff --git a/packages/hardware-trezor/src/TrezorKeyAgent.ts b/packages/hardware-trezor/src/TrezorKeyAgent.ts index 1f9838eadd4..725173c3482 100644 --- a/packages/hardware-trezor/src/TrezorKeyAgent.ts +++ b/packages/hardware-trezor/src/TrezorKeyAgent.ts @@ -203,14 +203,13 @@ export class TrezorKeyAgent extends KeyAgentBase { } async signTransaction( - tx: Cardano.TxBodyWithHash, + { body, hash }: Cardano.TxBodyWithHash, { knownAddresses, txInKeyPathMap }: SignTransactionContext ): Promise { try { await this.isTrezorInitialized; - const trezorTxData = txToTrezor({ + const trezorTxData = await txToTrezor(body, { accountIndex: this.accountIndex, - cardanoTxBody: tx.body, chainId: this.chainId, knownAddresses, txInKeyPathMap @@ -226,7 +225,7 @@ export class TrezorKeyAgent extends KeyAgentBase { const expectedPublicKeys = await Promise.all( util - .ownSignatureKeyPaths(tx.body, knownAddresses, txInKeyPathMap) + .ownSignatureKeyPaths(body, knownAddresses, txInKeyPathMap) .map((derivationPath) => this.derivePublicKey(derivationPath)) ); @@ -236,7 +235,7 @@ export class TrezorKeyAgent extends KeyAgentBase { const signedData = result.payload; - if (signedData.hash !== tx.hash) { + if (signedData.hash !== hash) { throw new errors.HwMappingError('Trezor computed a different transaction id'); } diff --git a/packages/hardware-trezor/src/transformers/additionalWitnessRequests.ts b/packages/hardware-trezor/src/transformers/additionalWitnessRequests.ts index 425219528a2..c57da368e38 100644 --- a/packages/hardware-trezor/src/transformers/additionalWitnessRequests.ts +++ b/packages/hardware-trezor/src/transformers/additionalWitnessRequests.ts @@ -1,20 +1,27 @@ import * as Trezor from '@trezor/connect'; -import { BIP32Path } from '@cardano-sdk/crypto'; +import { Cardano } from '@cardano-sdk/core'; +import { Transform, isNotNil } from '@cardano-sdk/util'; import { TrezorTxTransformerContext } from '../types'; -import { isNotNil } from '@cardano-sdk/util'; +import { resolvePaymentKeyPathForTxIn } from './keyPaths'; import { util } from '@cardano-sdk/key-management'; import isArray from 'lodash/isArray'; import uniq from 'lodash/uniq'; -export const mapAdditionalWitnessRequests = (inputs: Trezor.CardanoInput[], context: TrezorTxTransformerContext) => { - const paymentKeyPaths = uniq( +export const mapAdditionalWitnessRequests: Transform< + Cardano.TxIn[], + Trezor.DerivationPath[], + TrezorTxTransformerContext +> = (inputs, context) => { + const paymentKeyPaths = uniq( inputs - .map((i) => i.path) + .map((input) => resolvePaymentKeyPathForTxIn(input, context)) .filter(isNotNil) .filter(isArray) ); - const additionalWitnessPaths: BIP32Path[] = [...paymentKeyPaths]; - if (context.knownAddresses.length > 0) { + + const additionalWitnessPaths: Trezor.DerivationPath[] = [...paymentKeyPaths]; + + if (context?.knownAddresses?.length) { const stakeKeyPath = util.stakeKeyPathFromGroupedAddress(context.knownAddresses[0]); if (stakeKeyPath) additionalWitnessPaths.push(stakeKeyPath); } diff --git a/packages/hardware-trezor/src/transformers/assets.ts b/packages/hardware-trezor/src/transformers/assets.ts index 995c5786b65..56f944ec4cd 100644 --- a/packages/hardware-trezor/src/transformers/assets.ts +++ b/packages/hardware-trezor/src/transformers/assets.ts @@ -1,5 +1,6 @@ import * as Trezor from '@trezor/connect'; import { Cardano } from '@cardano-sdk/core'; +import { Transform } from '@cardano-sdk/util'; const compareAssetNameCanonically = (a: Trezor.CardanoToken, b: Trezor.CardanoToken) => { if (a.assetNameBytes.length === b.assetNameBytes.length) { @@ -12,7 +13,7 @@ const comparePolicyIdCanonically = (a: Trezor.CardanoAssetGroup, b: Trezor.Carda // PolicyId is always of the same length a.policyId > b.policyId ? 1 : -1; -const tokenMapToAssetGroup = (tokenMap: Cardano.TokenMap, isMint: boolean): Trezor.CardanoAssetGroup[] => { +const tokenMapToAssetGroup: Transform = (tokenMap, isMint) => { const map = new Map>(); for (const [key, value] of tokenMap.entries()) { diff --git a/packages/hardware-trezor/src/transformers/auxiliaryData.ts b/packages/hardware-trezor/src/transformers/auxiliaryData.ts index 4dad81f6fca..9e60682cee0 100644 --- a/packages/hardware-trezor/src/transformers/auxiliaryData.ts +++ b/packages/hardware-trezor/src/transformers/auxiliaryData.ts @@ -1,6 +1,10 @@ import * as Crypto from '@cardano-sdk/crypto'; import * as Trezor from '@trezor/connect'; +import { Transform } from '@cardano-sdk/util'; -export const mapAuxiliaryData = (auxiliaryDataHash: Crypto.Hash32ByteBase16): Trezor.CardanoAuxiliaryData => ({ +export const mapAuxiliaryData: Transform = ( + auxiliaryDataHash +) => ({ + cVoteRegistrationParameters: undefined, // Voting is not handled for now hash: auxiliaryDataHash }); diff --git a/packages/hardware-trezor/src/transformers/certificates.ts b/packages/hardware-trezor/src/transformers/certificates.ts index 23d3368acb3..490b88e0205 100644 --- a/packages/hardware-trezor/src/transformers/certificates.ts +++ b/packages/hardware-trezor/src/transformers/certificates.ts @@ -3,46 +3,15 @@ import * as Trezor from '@trezor/connect'; import { BIP32Path } from '@cardano-sdk/crypto'; import { Cardano } from '@cardano-sdk/core'; import { GroupedAddress, util } from '@cardano-sdk/key-management'; -import { InvalidArgumentError /* , Transform*/ } from '@cardano-sdk/util'; +import { InvalidArgumentError, Transform } from '@cardano-sdk/util'; import { TrezorTxTransformerContext } from '../types'; -type StakeKeyCertificateType = - | Trezor.PROTO.CardanoCertificateType.STAKE_REGISTRATION - | Trezor.PROTO.CardanoCertificateType.STAKE_DEREGISTRATION; - -type TrezorStakeKeyCertificate = { - type: StakeKeyCertificateType; - path?: BIP32Path; +type CertCredentialsType = { scriptHash?: Crypto.Ed25519KeyHashHex; keyHash?: Crypto.Ed25519KeyHashHex; -}; - -type TrezorDelegationCertificate = { - type: Trezor.PROTO.CardanoCertificateType.STAKE_DELEGATION; path?: BIP32Path; - scriptHash?: Crypto.Ed25519KeyHashHex; - pool: string; -}; - -type TrezorPoolRegistrationCertificate = { - poolParameters: Trezor.CardanoPoolParameters; - type: Trezor.PROTO.CardanoCertificateType.STAKE_POOL_REGISTRATION; -}; - -type ScriptHashCertCredentials = { - scriptHash: Crypto.Ed25519KeyHashHex; -}; - -type KeyHashCertCredentials = { - keyHash: Crypto.Ed25519KeyHashHex; }; -type PathCertCredentials = { - path: BIP32Path; -}; - -type CertCredentialsType = ScriptHashCertCredentials | KeyHashCertCredentials | PathCertCredentials; - const getCertCredentials = ( stakeKeyHash: Crypto.Ed25519KeyHashHex, knownAddresses: GroupedAddress[] | undefined @@ -61,57 +30,73 @@ const getCertCredentials = ( }; }; -const getStakeAddressCertificate = ( - certificate: Cardano.StakeAddressCertificate, - context: TrezorTxTransformerContext, - type: StakeKeyCertificateType -): TrezorStakeKeyCertificate => { +const toPoolMetadata = (metadataJson: Cardano.PoolMetadataJson): Trezor.CardanoPoolMetadata => ({ + hash: metadataJson.hash, + url: metadataJson.url +}); + +const getPoolOperatorKeyPath = ( + operator: Cardano.RewardAccount, + context: TrezorTxTransformerContext +): BIP32Path | null => { + const knownAddress = context?.knownAddresses.find((address) => address.rewardAccount === operator); + return util.stakeKeyPathFromGroupedAddress(knownAddress); +}; + +export const getStakeAddressCertificate: Transform< + Cardano.StakeAddressCertificate, + Trezor.CardanoCertificate, + TrezorTxTransformerContext +> = (certificate, context) => { const credentials = getCertCredentials( certificate.stakeCredential.hash as unknown as Crypto.Ed25519KeyHashHex, - context.knownAddresses + context?.knownAddresses ); + const certificateType = + certificate.__typename === Cardano.CertificateType.StakeRegistration + ? Trezor.PROTO.CardanoCertificateType.STAKE_REGISTRATION + : Trezor.PROTO.CardanoCertificateType.STAKE_DEREGISTRATION; return { - ...credentials, - type + keyHash: credentials.keyHash, + path: credentials.path, + pool: undefined, + poolParameters: undefined, + scriptHash: credentials.scriptHash, + type: certificateType }; }; -const getStakeDelegationCertificate = ( - certificate: Cardano.StakeDelegationCertificate, - context: TrezorTxTransformerContext -): TrezorDelegationCertificate => { +export const getStakeDelegationCertificate: Transform< + Cardano.StakeDelegationCertificate, + Trezor.CardanoCertificate, + TrezorTxTransformerContext +> = (certificate, context) => { const poolIdKeyHash = Cardano.PoolId.toKeyHash(certificate.poolId); const credentials = getCertCredentials( certificate.stakeCredential.hash as unknown as Crypto.Ed25519KeyHashHex, - context.knownAddresses + context?.knownAddresses ); return { - ...credentials, + keyHash: credentials.keyHash, + path: credentials.path, pool: poolIdKeyHash, + poolParameters: undefined, + scriptHash: credentials.scriptHash, type: Trezor.PROTO.CardanoCertificateType.STAKE_DELEGATION }; }; -const toPoolMetadata = (metadataJson: Cardano.PoolMetadataJson): Trezor.CardanoPoolMetadata => ({ - hash: metadataJson.hash, - url: metadataJson.url -}); - -const getPoolOperatorKeyPath = ( - operator: Cardano.RewardAccount, - context: TrezorTxTransformerContext -): BIP32Path | null => { - const knownAddress = context?.knownAddresses.find((address) => address.rewardAccount === operator); - return util.stakeKeyPathFromGroupedAddress(knownAddress); -}; - -export const getPoolRegistrationCertificate = ( - certificate: Cardano.PoolRegistrationCertificate, - context: TrezorTxTransformerContext -): TrezorPoolRegistrationCertificate => { +export const getPoolRegistrationCertificate: Transform< + Cardano.PoolRegistrationCertificate, + Trezor.CardanoCertificate, + TrezorTxTransformerContext +> = (certificate, context) => { if (!certificate.poolParameters.metadataJson) throw new InvalidArgumentError('certificate', 'Missing pool registration pool metadata.'); return { + keyHash: undefined, + path: undefined, + pool: undefined, poolParameters: { cost: certificate.poolParameters.cost.toString(), margin: { @@ -153,6 +138,7 @@ export const getPoolRegistrationCertificate = ( rewardAccount: certificate.poolParameters.rewardAccount, vrfKeyHash: certificate.poolParameters.vrf }, + scriptHash: undefined, type: Trezor.PROTO.CardanoCertificateType.STAKE_POOL_REGISTRATION }; }; @@ -160,9 +146,9 @@ export const getPoolRegistrationCertificate = ( const toCert = (cert: Cardano.Certificate, context: TrezorTxTransformerContext) => { switch (cert.__typename) { case Cardano.CertificateType.StakeRegistration: - return getStakeAddressCertificate(cert, context, Trezor.PROTO.CardanoCertificateType.STAKE_REGISTRATION); + return getStakeAddressCertificate(cert, context); case Cardano.CertificateType.StakeDeregistration: - return getStakeAddressCertificate(cert, context, Trezor.PROTO.CardanoCertificateType.STAKE_DEREGISTRATION); + return getStakeAddressCertificate(cert, context); case Cardano.CertificateType.StakeDelegation: return getStakeDelegationCertificate(cert, context); case Cardano.CertificateType.PoolRegistration: diff --git a/packages/hardware-trezor/src/transformers/keyPaths.ts b/packages/hardware-trezor/src/transformers/keyPaths.ts index 0e6d7c32cd1..3b2ab27991e 100644 --- a/packages/hardware-trezor/src/transformers/keyPaths.ts +++ b/packages/hardware-trezor/src/transformers/keyPaths.ts @@ -1,7 +1,7 @@ import { BIP32Path } from '@cardano-sdk/crypto'; import { Cardano } from '@cardano-sdk/core'; +import { GroupedAddress, TxInId, util } from '@cardano-sdk/key-management'; import { TrezorTxTransformerContext } from '../types'; -import { TxInId, util } from '@cardano-sdk/key-management'; /** Uses the given Trezor input resolver to resolve the payment key path for known addresses for given input transaction. */ export const resolvePaymentKeyPathForTxIn = ( @@ -15,13 +15,11 @@ export const resolvePaymentKeyPathForTxIn = ( } }; -// Resolves the stake key path for known addresses for the given reward address. export const resolveStakeKeyPath = ( - rewardAddress: Cardano.RewardAddress | undefined, - context: TrezorTxTransformerContext + rewardAddress: Cardano.RewardAddress, + knownAddresses: GroupedAddress[] ): BIP32Path | null => { - if (!rewardAddress) return null; - const knownAddress = context.knownAddresses.find( + const knownAddress = knownAddresses.find( ({ rewardAccount }) => rewardAccount === rewardAddress.toAddress().toBech32() ); return util.stakeKeyPathFromGroupedAddress(knownAddress); diff --git a/packages/hardware-trezor/src/transformers/requiredSigners.ts b/packages/hardware-trezor/src/transformers/requiredSigners.ts index 791eb036110..65f7afbd313 100644 --- a/packages/hardware-trezor/src/transformers/requiredSigners.ts +++ b/packages/hardware-trezor/src/transformers/requiredSigners.ts @@ -1,21 +1,23 @@ import * as Crypto from '@cardano-sdk/crypto'; import * as Trezor from '@trezor/connect'; import { Cardano } from '@cardano-sdk/core'; +import { Transform } from '@cardano-sdk/util'; import { TrezorTxTransformerContext } from '../types'; import { util } from '@cardano-sdk/key-management'; -export const toRequiredSigner = ( - signer: Crypto.Ed25519KeyHashHex, - context: TrezorTxTransformerContext -): Trezor.CardanoRequiredSigner => { +export const toRequiredSigner: Transform< + Crypto.Ed25519KeyHashHex, + Trezor.CardanoRequiredSigner, + TrezorTxTransformerContext +> = (keyHash, context) => { const paymentCredKnownAddress = context?.knownAddresses.find((address) => { const paymentCredential = Cardano.Address.fromBech32(address.address)?.asBase()?.getPaymentCredential().hash; - return paymentCredential && paymentCredential.toString() === signer; + return paymentCredential && paymentCredential.toString() === keyHash; }); const stakeCredKnownAddress = context?.knownAddresses.find((address) => { const stakeCredential = Cardano.RewardAccount.toHash(address.rewardAccount); - return stakeCredential && stakeCredential.toString() === signer; + return stakeCredential && stakeCredential.toString() === keyHash; }); const paymentKeyPath = paymentCredKnownAddress @@ -23,7 +25,24 @@ export const toRequiredSigner = ( : null; const stakeKeyPath = stakeCredKnownAddress ? util.stakeKeyPathFromGroupedAddress(stakeCredKnownAddress) : null; - return paymentKeyPath ? { keyPath: paymentKeyPath } : stakeKeyPath ? { keyPath: stakeKeyPath } : { keyHash: signer }; + if (paymentKeyPath) { + return { + keyHash: undefined, + keyPath: paymentKeyPath + }; + } + + if (stakeKeyPath) { + return { + keyHash: undefined, + keyPath: stakeKeyPath + }; + } + + return { + keyHash, + keyPath: undefined + }; }; export const mapRequiredSigners = ( diff --git a/packages/hardware-trezor/src/transformers/tx.ts b/packages/hardware-trezor/src/transformers/tx.ts index 84eef1cdea7..58e30fa8ac0 100644 --- a/packages/hardware-trezor/src/transformers/tx.ts +++ b/packages/hardware-trezor/src/transformers/tx.ts @@ -1,50 +1,38 @@ import * as Trezor from '@trezor/connect'; import { Cardano } from '@cardano-sdk/core'; +import { Transformer, transformObj } from '@cardano-sdk/util'; import { TrezorTxTransformerContext } from '../types'; import { mapAdditionalWitnessRequests } from './additionalWitnessRequests'; import { mapAuxiliaryData, mapCerts, mapRequiredSigners, mapTxIns, mapTxOuts, mapWithdrawals, toTxOut } from './'; import { mapTokenMap } from './assets'; -/** - * Temporary transformer function that returns a partial - * trezor.CardanoSignTransaction object which can be merged - * into the extisting implementation (later we should refactor - * this function to the Transformer interface like in the - * hardware-ledger package) - */ -const trezorTxTransformer = ( - body: Cardano.TxBody, - context: TrezorTxTransformerContext -): Omit => { - const inputs = mapTxIns(body.inputs, context); - return { - additionalWitnessRequests: mapAdditionalWitnessRequests(inputs, context), - auxiliaryData: body.auxiliaryDataHash ? mapAuxiliaryData(body.auxiliaryDataHash) : undefined, - certificates: mapCerts(body.certificates ?? [], context), - collateralInputs: body.collaterals ? mapTxIns(body.collaterals, context) : undefined, - collateralReturn: body.collateralReturn ? toTxOut(body.collateralReturn, context) : undefined, - fee: body.fee.toString(), - inputs, - mint: mapTokenMap(body.mint, true), - networkId: context.chainId.networkId, - outputs: mapTxOuts(body.outputs, context), - protocolMagic: context.chainId.networkMagic, - referenceInputs: body.referenceInputs ? mapTxIns(body.referenceInputs, context) : undefined, - requiredSigners: body.requiredExtraSignatures - ? mapRequiredSigners(body.requiredExtraSignatures, context) - : undefined, - totalCollateral: body.totalCollateral ? body.totalCollateral.toString() : undefined, - ttl: body.validityInterval?.invalidHereafter?.toString(), - validityIntervalStart: body.validityInterval?.invalidBefore?.toString(), - withdrawals: mapWithdrawals(body.withdrawals ?? [], context) - }; +export const trezorTxTransformer: Transformer< + Cardano.TxBody, + Omit, + TrezorTxTransformerContext +> = { + additionalWitnessRequests: ({ inputs }, context) => mapAdditionalWitnessRequests(inputs, context!), + auxiliaryData: ({ auxiliaryDataHash }) => (auxiliaryDataHash ? mapAuxiliaryData(auxiliaryDataHash) : undefined), + certificates: ({ certificates }, context) => (certificates ? mapCerts(certificates, context!) : undefined), + collateralInputs: ({ collaterals }, context) => (collaterals ? mapTxIns(collaterals, context!) : undefined), + collateralReturn: ({ collateralReturn }, context) => + collateralReturn ? toTxOut(collateralReturn, context!) : undefined, + fee: ({ fee }) => fee.toString(), + inputs: ({ inputs }, context) => mapTxIns(inputs, context!), + mint: ({ mint }) => mapTokenMap(mint, true), + networkId: (_, context) => context!.chainId.networkId, + outputs: ({ outputs }, context) => mapTxOuts(outputs, context!), + protocolMagic: (_, context) => context!.chainId.networkMagic, + referenceInputs: ({ referenceInputs }, context) => + referenceInputs ? mapTxIns(referenceInputs, context!) : undefined, + requiredSigners: ({ requiredExtraSignatures }, context) => + requiredExtraSignatures ? mapRequiredSigners(requiredExtraSignatures, context!) : undefined, + scriptDataHash: ({ scriptIntegrityHash }) => scriptIntegrityHash?.toString(), + totalCollateral: ({ totalCollateral }) => totalCollateral?.toString(), + ttl: ({ validityInterval }) => validityInterval?.invalidHereafter?.toString(), + validityIntervalStart: ({ validityInterval }) => validityInterval?.invalidBefore?.toString(), + withdrawals: ({ withdrawals }, context) => mapWithdrawals(withdrawals, context!) }; -/** Takes a core transaction and context data necessary to transform it into a trezor.CardanoSignTransaction */ -export const txToTrezor = ({ - cardanoTxBody, - ...context -}: { - cardanoTxBody: Cardano.TxBody; -} & TrezorTxTransformerContext): Omit => - trezorTxTransformer(cardanoTxBody, context); +export const txToTrezor = (body: Cardano.TxBody, context: TrezorTxTransformerContext) => + transformObj(body, trezorTxTransformer, context); diff --git a/packages/hardware-trezor/src/transformers/txOut.ts b/packages/hardware-trezor/src/transformers/txOut.ts index 252d4361f55..38524a222ec 100644 --- a/packages/hardware-trezor/src/transformers/txOut.ts +++ b/packages/hardware-trezor/src/transformers/txOut.ts @@ -31,10 +31,10 @@ const toDestination: Transform { +const getScriptHex = (output: Serialization.TransactionOutput): HexBlob | undefined => { const scriptRef = output.scriptRef(); - if (!scriptRef) return null; + if (!scriptRef) return undefined; return scriptRef.toCbor(); }; @@ -69,37 +69,30 @@ const isBabbage = (out: Serialization.TransactionOutput): boolean => { const getInlineDatum = (datum: Cardano.PlutusData): string => Serialization.PlutusData.fromCore(datum).toCbor(); -// TODO - use Transform (@cardano-sdk/util) once it is fixed. Even if prop is marked as optional it has to be added to fullfil Transform rules e.g. datumHash -export const toTxOut = (txOut: Cardano.TxOut, context: TrezorTxTransformerContext): Trezor.CardanoOutput => { +export const toTxOut: Transform = (txOut, context) => { const destination = toDestination(txOut, context); const output = Serialization.TransactionOutput.fromCore(txOut); - const referenceScriptHex = getScriptHex(output); + const scriptHex = getScriptHex(output); - const trezorTxOut = isBabbage(output) + return isBabbage(output) ? { ...destination, - ...(txOut.datumHash - ? { datumHash: txOut.datumHash.toString() } - : txOut.datum - ? { inlineDatum: getInlineDatum(txOut.datum) } - : undefined), - ...(referenceScriptHex && { referenceScript: referenceScriptHex }), amount: txOut.value.coins.toString(), - format: Trezor.PROTO.CardanoTxOutputSerializationFormat.MAP_BABBAGE + datumHash: txOut.datumHash?.toString(), + format: Trezor.PROTO.CardanoTxOutputSerializationFormat.MAP_BABBAGE, + inlineDatum: txOut.datum ? getInlineDatum(txOut.datum) : undefined, + referenceScript: scriptHex, + tokenBundle: mapTokenMap(txOut.value.assets) } : { ...destination, - ...(txOut.datumHash && { datumHash: txOut.datumHash.toString() }), amount: txOut.value.coins.toString(), - format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY + datumHash: txOut.datumHash?.toString(), + format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY, + inlineDatum: undefined, + referenceScript: undefined, + tokenBundle: mapTokenMap(txOut.value.assets) }; - - if (txOut.value.assets) { - const tokenBundle = mapTokenMap(txOut.value.assets); - Object.assign(trezorTxOut, { tokenBundle }); - } - - return trezorTxOut; }; export const mapTxOuts = (txOuts: Cardano.TxOut[], context: TrezorTxTransformerContext): Trezor.CardanoOutput[] => diff --git a/packages/hardware-trezor/src/transformers/withdrawals.ts b/packages/hardware-trezor/src/transformers/withdrawals.ts index c8bc0cf693d..40fe67113db 100644 --- a/packages/hardware-trezor/src/transformers/withdrawals.ts +++ b/packages/hardware-trezor/src/transformers/withdrawals.ts @@ -1,13 +1,13 @@ import * as Trezor from '@trezor/connect'; import { Cardano } from '@cardano-sdk/core'; -import { InvalidArgumentError } from '@cardano-sdk/util'; +import { InvalidArgumentError, Transform } from '@cardano-sdk/util'; import { TrezorTxTransformerContext } from '../types'; import { resolveStakeKeyPath } from './keyPaths'; -export const toTrezorWithdrawal = ( - withdrawal: Cardano.Withdrawal, - context: TrezorTxTransformerContext -): Trezor.CardanoWithdrawal => { +export const toTrezorWithdrawal: Transform = ( + withdrawal, + context +) => { const address = Cardano.Address.fromString(withdrawal.stakeAddress); const rewardAddress = address?.asReward(); @@ -22,19 +22,25 @@ export const toTrezorWithdrawal = ( * - The script hash, blake2b-224 hash digests of serialized monetary scripts. */ if (rewardAddress.getPaymentCredential().type === Cardano.CredentialType.KeyHash) { - const keyPath = resolveStakeKeyPath(rewardAddress, context); + const keyPath = context?.knownAddresses ? resolveStakeKeyPath(rewardAddress, context.knownAddresses) : null; trezorWithdrawal = keyPath ? { amount: withdrawal.quantity.toString(), - path: keyPath + keyHash: undefined, + path: keyPath, + scriptHash: undefined } : { amount: withdrawal.quantity.toString(), - keyHash: rewardAddress.getPaymentCredential().hash.toString() + keyHash: rewardAddress.getPaymentCredential().hash.toString(), + path: undefined, + scriptHash: undefined }; } else { trezorWithdrawal = { amount: withdrawal.quantity.toString(), + keyHash: undefined, + path: undefined, scriptHash: rewardAddress.getPaymentCredential().hash.toString() }; } @@ -43,6 +49,7 @@ export const toTrezorWithdrawal = ( }; export const mapWithdrawals = ( - withdrawals: Cardano.Withdrawal[], + withdrawals: Cardano.Withdrawal[] | undefined, context: TrezorTxTransformerContext -): Trezor.CardanoWithdrawal[] => withdrawals.map((coreWithdrawal) => toTrezorWithdrawal(coreWithdrawal, context)); +): Trezor.CardanoWithdrawal[] | undefined => + withdrawals ? withdrawals.map((coreWithdrawal) => toTrezorWithdrawal(coreWithdrawal, context)) : undefined; diff --git a/packages/hardware-trezor/test/testData.ts b/packages/hardware-trezor/test/testData.ts index f01c0667370..7726bf407a4 100644 --- a/packages/hardware-trezor/test/testData.ts +++ b/packages/hardware-trezor/test/testData.ts @@ -115,7 +115,7 @@ export const txOutWithReferenceScriptAndInlineDatum: Cardano.TxOut = { export const rewardKey = 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'; export const rewardScript = 'stake178phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcccycj5'; export const rewardAccount = Cardano.RewardAccount(rewardKey); -export const rewardAddress = Cardano.Address.fromBech32(rewardAccount)?.asReward(); +export const rewardAddress = Cardano.Address.fromBech32(rewardAccount)?.asReward() as Cardano.RewardAddress; export const rewardAccountWithPaymentScriptCredential = Cardano.RewardAccount(rewardScript); export const stakeKeyHash = Cardano.RewardAccount.toHash(rewardAccount); export const stakeCredential = { diff --git a/packages/hardware-trezor/test/transformers/additionalWitnessRequests.test.ts b/packages/hardware-trezor/test/transformers/additionalWitnessRequests.test.ts index 49aba2aa782..2311ad2c30d 100644 --- a/packages/hardware-trezor/test/transformers/additionalWitnessRequests.test.ts +++ b/packages/hardware-trezor/test/transformers/additionalWitnessRequests.test.ts @@ -1,16 +1,14 @@ import { TxInId } from '@cardano-sdk/key-management'; import { contextWithKnownAddresses, txIn } from '../testData'; import { mapAdditionalWitnessRequests } from '../../src/transformers/additionalWitnessRequests'; -import { toTrezorTxIn } from '../../src'; describe('additionalWitnessRequests', () => { it('should include payment key paths and reward account key path from given inputs', async () => { const paymentKeyPath = { index: 0, role: 1 }; - const mappedTrezorTxIn = toTrezorTxIn(txIn, { + const result = mapAdditionalWitnessRequests([txIn], { ...contextWithKnownAddresses, txInKeyPathMap: { [TxInId(txIn)]: paymentKeyPath } }); - const result = mapAdditionalWitnessRequests([mappedTrezorTxIn], contextWithKnownAddresses); expect(result).toEqual([ [2_147_485_500, 2_147_485_463, 2_147_483_648, paymentKeyPath.role, paymentKeyPath.index], [2_147_485_500, 2_147_485_463, 2_147_483_648, 2, 0] // reward account key path diff --git a/packages/hardware-trezor/test/transformers/keyPaths.test.ts b/packages/hardware-trezor/test/transformers/keyPaths.test.ts index 4c1928e9177..8c7efc9ffd4 100644 --- a/packages/hardware-trezor/test/transformers/keyPaths.test.ts +++ b/packages/hardware-trezor/test/transformers/keyPaths.test.ts @@ -22,7 +22,9 @@ describe('key-paths', () => { }); describe('resolveStakeKeyPath', () => { it('returns the stake key path for a known address', async () => { - expect(resolveStakeKeyPath(rewardAddress, contextWithKnownAddresses)).toEqual(knownAddressStakeKeyPath); + expect(resolveStakeKeyPath(rewardAddress, contextWithKnownAddresses.knownAddresses)).toEqual( + knownAddressStakeKeyPath + ); }); }); }); diff --git a/packages/hardware-trezor/test/transformers/tx.test.ts b/packages/hardware-trezor/test/transformers/tx.test.ts index b07f6c0e065..c8e2688e4c6 100644 --- a/packages/hardware-trezor/test/transformers/tx.test.ts +++ b/packages/hardware-trezor/test/transformers/tx.test.ts @@ -17,16 +17,9 @@ import { txToTrezor } from '../../src/transformers/tx'; describe('tx', () => { describe('txToTrezor', () => { - test('can map min valid transaction', () => { - expect( - txToTrezor({ - ...contextWithoutKnownAddresses, - cardanoTxBody: minValidTxBody - }) - ).toEqual({ + test('can map min valid transaction', async () => { + expect(await txToTrezor(minValidTxBody, contextWithoutKnownAddresses)).toEqual({ additionalWitnessRequests: [], - auxiliaryData: undefined, - certificates: [], fee: '10', inputs: [ { @@ -34,7 +27,6 @@ describe('tx', () => { prev_index: txIn.index } ], - mint: undefined, networkId: 0, outputs: [ { @@ -44,18 +36,14 @@ describe('tx', () => { format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY } ], - protocolMagic: 999, - ttl: undefined, - validityIntervalStart: undefined, - withdrawals: [] + protocolMagic: 999 }); }); - test('can map transaction without scripts', () => { + test('can map transaction without scripts', async () => { expect( - txToTrezor({ + await txToTrezor(txBody, { ...contextWithKnownAddresses, - cardanoTxBody: txBody, txInKeyPathMap: { [TxInId(txBody.inputs[0])]: knownAddressPaymentKeyPath } }) ).toEqual({ @@ -208,11 +196,10 @@ describe('tx', () => { }); }); - test('can map babbage transaction with scripts', () => { + test('can map babbage transaction with scripts', async () => { expect( - txToTrezor({ + await txToTrezor(babbageTxBodyWithScripts, { ...contextWithKnownAddresses, - cardanoTxBody: babbageTxBodyWithScripts, txInKeyPathMap: { [TxInId(babbageTxBodyWithScripts.inputs[0])]: knownAddressPaymentKeyPath } @@ -294,16 +281,9 @@ describe('tx', () => { }); }); - test('can map transaction with collaterals', () => { - expect( - txToTrezor({ - ...contextWithoutKnownAddresses, - cardanoTxBody: txBodyWithCollaterals - }) - ).toEqual({ + test('can map transaction with collaterals', async () => { + expect(await txToTrezor(txBodyWithCollaterals, contextWithoutKnownAddresses)).toEqual({ additionalWitnessRequests: [], - auxiliaryData: undefined, - certificates: [], collateralInputs: [ { prev_hash: txIn.txId, @@ -323,7 +303,6 @@ describe('tx', () => { prev_index: txIn.index } ], - mint: undefined, networkId: 0, outputs: [ { @@ -333,18 +312,14 @@ describe('tx', () => { format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY } ], - protocolMagic: 999, - ttl: undefined, - validityIntervalStart: undefined, - withdrawals: [] + protocolMagic: 999 }); }); - test('can map plutus transaction with babbage elements', () => { + test('can map plutus transaction with babbage elements', async () => { expect( - txToTrezor({ + await txToTrezor(plutusTxWithBabbage, { ...contextWithKnownAddresses, - cardanoTxBody: plutusTxWithBabbage, txInKeyPathMap: { [TxInId(plutusTxWithBabbage.inputs[0])]: knownAddressPaymentKeyPath, [TxInId(plutusTxWithBabbage.collaterals[0])]: knownAddressPaymentKeyPath