Skip to content

Commit 5b4680d

Browse files
authored
Merge pull request #1652 from input-output-hk/feat/fix-trezor-signing
Feat/fix trezor signing
2 parents ec23c9e + 92b19e2 commit 5b4680d

File tree

6 files changed

+103
-2
lines changed

6 files changed

+103
-2
lines changed

packages/hardware-ledger/src/LedgerKeyAgent.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import _LedgerConnection, {
5353
Transaction,
5454
TransactionSigningMode,
5555
TxOutputDestinationType,
56+
TxRequiredSignerType,
5657
VoterType,
5758
VoterVotes
5859
} from '@cardano-foundation/ledgerjs-hw-app-cardano';
@@ -207,10 +208,12 @@ const isMultiSig = (tx: Transaction): boolean => {
207208
const result = false;
208209

209210
const allThirdPartyInputs = !tx.inputs.some((input) => input.path !== null);
211+
const allThirdPartyRequiredSigner = !tx.requiredSigners?.some((signer) => signer.type === TxRequiredSignerType.PATH);
210212
// Ledger doesn't allow change outputs to address controlled by your keys and instead you have to use script address for change out
211213
const allThirdPartyOutputs = !tx.outputs.some((out) => out.destination.type !== TxOutputDestinationType.THIRD_PARTY);
212214

213215
if (
216+
allThirdPartyRequiredSigner &&
214217
allThirdPartyInputs &&
215218
allThirdPartyOutputs &&
216219
!tx.collateralInputs &&

packages/hardware-ledger/test/LedgerKeyAgent.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,56 @@ describe('LedgerKeyAgent', () => {
359359

360360
expect(LedgerKeyAgent.getSigningMode(tx)).toEqual(Ledger.TransactionSigningMode.MULTISIG_TRANSACTION);
361361
});
362+
363+
it('can detect ordinary transaction signing mode when we own a required signer', async () => {
364+
const tx: Ledger.Transaction = {
365+
certificates: [
366+
{
367+
params: {
368+
stakeCredential: {
369+
scriptHashHex: 'cb0ec2692497b458e46812c8a5bfa2931d1a2d965a99893828ec810f',
370+
type: Ledger.CredentialParamsType.SCRIPT_HASH
371+
}
372+
},
373+
type: Ledger.CertificateType.STAKE_DEREGISTRATION
374+
}
375+
],
376+
fee: 10n,
377+
includeNetworkId: false,
378+
inputs: [
379+
{
380+
outputIndex: 0,
381+
path: null,
382+
txHashHex: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f190000'
383+
}
384+
],
385+
network: {
386+
networkId: Ledger.Networks.Testnet.networkId,
387+
protocolMagic: 999
388+
},
389+
outputs: [
390+
{
391+
amount: 10n,
392+
datumHashHex: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5',
393+
destination: {
394+
params: {
395+
addressHex:
396+
'009493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc'
397+
},
398+
type: Ledger.TxOutputDestinationType.THIRD_PARTY
399+
},
400+
format: Ledger.TxOutputFormat.ARRAY_LEGACY
401+
}
402+
],
403+
requiredSigners: [
404+
{ path: [util.harden(1852), util.harden(1815), util.harden(0), 2, 0], type: Ledger.TxRequiredSignerType.PATH }
405+
],
406+
ttl: 1000,
407+
validityIntervalStart: 100
408+
};
409+
410+
expect(LedgerKeyAgent.getSigningMode(tx)).toEqual(Ledger.TransactionSigningMode.ORDINARY_TRANSACTION);
411+
});
362412
});
363413

364414
describe('Unsupported Transaction Errors', () => {

packages/hardware-trezor/src/TrezorKeyAgent.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,14 @@ const multiSigWitnessPaths: BIP32Path[] = [
9292

9393
const isMultiSig = (tx: Omit<Trezor.CardanoSignTransaction, 'signingMode'>): boolean => {
9494
const allThirdPartyInputs = !tx.inputs.some((input) => input.path);
95+
const allThirdRequiredSigner = !tx.requiredSigners?.some((signer) => signer.keyPath);
9596
// Trezor doesn't allow change outputs to address controlled by your keys and instead you have to use script address for change out
9697
const allThirdPartyOutputs = !tx.outputs.some((out) => 'addressParameters' in out);
9798

9899
return (
99100
allThirdPartyInputs &&
100101
allThirdPartyOutputs &&
102+
allThirdRequiredSigner &&
101103
!tx.collateralInputs &&
102104
!tx.collateralReturn &&
103105
!tx.totalCollateral &&
@@ -325,7 +327,9 @@ export class TrezorKeyAgent extends KeyAgentBase {
325327
const signedData = result.payload;
326328

327329
if (!areStringsEqualInConstantTime(signedData.hash, hash)) {
328-
throw new errors.HwMappingError('Trezor computed a different transaction id');
330+
throw new errors.HwMappingError(
331+
`Trezor computed a different transaction id: ${signedData.hash} than expected: ${hash}`
332+
);
329333
}
330334

331335
return new Map<Crypto.Ed25519PublicKeyHex, Crypto.Ed25519SignatureHex>(
@@ -340,7 +344,7 @@ export class TrezorKeyAgent extends KeyAgentBase {
340344
)
341345
);
342346
} catch (error: any) {
343-
if (error.innerError.code === 'Failure_ActionCancelled') {
347+
if (error.innerError?.code === 'Failure_ActionCancelled') {
344348
throw new errors.AuthenticationError('Transaction signing aborted', error);
345349
}
346350
throw transportTypedError(error);

packages/hardware-trezor/src/transformers/tx.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export const trezorTxTransformer: Transformer<
1111
Omit<Trezor.CardanoSignTransaction, 'signingMode' | 'derivationType' | 'includeNetworkId' | 'chunkify' | 'ttl'> & {
1212
/* eslint-disable @typescript-eslint/no-explicit-any */
1313
ttl: any; // TODO: the Transformer util cant handle ttl as TOptional<string | number>
14+
/* eslint-disable @typescript-eslint/no-explicit-any */
15+
includeNetworkId: any; // TODO: the Transformer util cant handle TOptional<string | boolean>
1416
},
1517
TrezorTxTransformerContext
1618
> = {
@@ -21,6 +23,7 @@ export const trezorTxTransformer: Transformer<
2123
collateralReturn: ({ collateralReturn }, context) =>
2224
collateralReturn ? toTxOut({ index: 0, isCollateral: true, txOut: collateralReturn }, context!) : undefined,
2325
fee: ({ fee }) => fee.toString(),
26+
includeNetworkId: ({ networkId }) => !!networkId,
2427
inputs: ({ inputs }, context) => mapTxIns(inputs, context!),
2528
mint: ({ mint }) => mapTokenMap(mint, true),
2629
networkId: (_, context) => context!.chainId.networkId,

packages/hardware-trezor/test/TrezorKeyAgent.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ describe('TrezorKeyAgent', () => {
125125
expect(signingMode).toEqual(Trezor.PROTO.CardanoTxSigningMode.ORDINARY_TRANSACTION);
126126
});
127127

128+
it('can detect ordinary transaction signing mode when we own a required signer', async () => {
129+
const signingMode = TrezorKeyAgent.matchSigningMode({
130+
...validMultisigTx,
131+
requiredSigners: [{ keyPath: knownAddressKeyPath }]
132+
});
133+
expect(signingMode).toEqual(Trezor.PROTO.CardanoTxSigningMode.ORDINARY_TRANSACTION);
134+
});
135+
128136
it('can detect pool registrations signing mode', async () => {
129137
const signingMode = TrezorKeyAgent.matchSigningMode({
130138
...simpleTx,

packages/hardware-trezor/test/transformers/tx.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as Trezor from '@trezor/connect';
2+
import { Cardano } from '@cardano-sdk/core';
23
import { CardanoKeyConst, TxInId, util } from '@cardano-sdk/key-management';
34
import {
45
babbageTxBodyWithScripts,
@@ -22,6 +23,34 @@ describe('tx', () => {
2223
expect(await txToTrezor(minValidTxBody, contextWithoutKnownAddresses)).toEqual({
2324
additionalWitnessRequests: [],
2425
fee: '10',
26+
includeNetworkId: false,
27+
inputs: [
28+
{
29+
prev_hash: txIn.txId,
30+
prev_index: txIn.index
31+
}
32+
],
33+
networkId: 0,
34+
outputs: [
35+
{
36+
address:
37+
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
38+
amount: '10',
39+
format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY
40+
}
41+
],
42+
protocolMagic: 999,
43+
tagCborSets: false
44+
});
45+
});
46+
47+
test('can set includeNetworkId if network field is set', async () => {
48+
expect(
49+
await txToTrezor({ ...minValidTxBody, networkId: Cardano.NetworkId.Mainnet }, contextWithoutKnownAddresses)
50+
).toEqual({
51+
additionalWitnessRequests: [],
52+
fee: '10',
53+
includeNetworkId: true,
2554
inputs: [
2655
{
2756
prev_hash: txIn.txId,
@@ -69,6 +98,7 @@ describe('tx', () => {
6998
}
7099
],
71100
fee: '10',
101+
includeNetworkId: false,
72102
inputs: [
73103
{
74104
path: knownAddressKeyPath,
@@ -232,6 +262,7 @@ describe('tx', () => {
232262
}
233263
],
234264
fee: '10',
265+
includeNetworkId: false,
235266
inputs: [
236267
{
237268
path: knownAddressKeyPath,
@@ -315,6 +346,7 @@ describe('tx', () => {
315346
format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY
316347
},
317348
fee: '10',
349+
includeNetworkId: false,
318350
inputs: [
319351
{
320352
prev_hash: txIn.txId,
@@ -385,6 +417,7 @@ describe('tx', () => {
385417
format: Trezor.PROTO.CardanoTxOutputSerializationFormat.MAP_BABBAGE
386418
},
387419
fee: '10',
420+
includeNetworkId: false,
388421
inputs: [
389422
{
390423
path: knownAddressKeyPath,

0 commit comments

Comments
 (0)