Skip to content

Commit a7c5b18

Browse files
Merge pull request #1616 from input-output-hk/fix/output-mapping-on-hardware-wallets
LW-12624: Fix output mapping on hardware wallets
2 parents 17add5a + 3de9a1d commit a7c5b18

File tree

16 files changed

+103
-85
lines changed

16 files changed

+103
-85
lines changed

packages/core/src/Serialization/TransactionBody/TransactionBody.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,19 @@ export class TransactionBody {
947947
return reader.peekState() === CborReaderState.Tag && reader.peekTag() === CborTag.Set;
948948
}
949949

950+
/**
951+
* Checks if the transaction body has Babbage outputs.
952+
*
953+
* @returns true if the transaction body has Babbage outputs, false otherwise.
954+
*/
955+
hasBabbageOutput(): boolean {
956+
if (this.#outputs.length === 0) return false;
957+
958+
const reader = new CborReader(this.#outputs[0].toCbor());
959+
960+
return reader.peekState() === CborReaderState.StartMap;
961+
}
962+
950963
/**
951964
* Gets the size of the serialized map.
952965
*

packages/core/test/Serialization/TransactionBody/TransactionBody.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import * as Cardano from '../../../src/Cardano';
33
import * as Crypto from '@cardano-sdk/crypto';
44
import { HexBlob } from '@cardano-sdk/util';
5-
import { TransactionBody, TxBodyCBOR, TxCBOR } from '../../../src/Serialization';
5+
import { Transaction, TransactionBody, TxBodyCBOR, TxCBOR } from '../../../src/Serialization';
66
import { babbageTx } from '../testData';
77
import { mintTokenMap, params, txIn, txOut } from './testData';
88

@@ -273,6 +273,24 @@ describe('TransactionBody', () => {
273273
expect(body.toCore()).toEqual(expectedConwayCore);
274274
});
275275

276+
it('can encode identify transactions output format - Babbage outputs', () => {
277+
const tx = Transaction.fromCbor(
278+
TxCBOR(
279+
'84a500818258207aa1264bcd0c06f34a49ed1dd7307a2bdec5a97bdeb546498759ad5b8ed42fd5010182a200583930195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d66195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d66011a00e4e1c0a2005839003d3246dc0c50ab3c74a8ffdd8068313ff99d341c461a8fe31f416d0a8fba06d60d71edc077cc5ebcb8ff82137afbce68df98271909332348011b000000025106a838021a00029309031a04a07bc6081a04a07a40a0f5f6'
280+
)
281+
);
282+
expect(tx.body().hasBabbageOutput()).toBeTruthy();
283+
});
284+
285+
it('can encode identify transactions output format - Legacy outputs', () => {
286+
const tx = Transaction.fromCbor(
287+
TxCBOR(
288+
'84a500818258207aa1264bcd0c06f34a49ed1dd7307a2bdec5a97bdeb546498759ad5b8ed42fd501018282583930195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d66195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d661a00e4e1c0825839003d3246dc0c50ab3c74a8ffdd8068313ff99d341c461a8fe31f416d0a8fba06d60d71edc077cc5ebcb8ff82137afbce68df982719093323481b000000025106a838021a00029309031a04a07bc6081a04a07a40a0f5f6'
289+
)
290+
);
291+
expect(tx.body().hasBabbageOutput()).toBeFalsy();
292+
});
293+
276294
it('sorts withdrawals canonically', () => {
277295
const body = TransactionBody.fromCbor(cbor);
278296
const withdrawals = body.withdrawals();

packages/hardware-ledger/src/LedgerKeyAgent.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,15 +715,18 @@ export class LedgerKeyAgent extends KeyAgentBase {
715715
): Promise<Cardano.Signatures> {
716716
try {
717717
const body = txBody.toCore();
718+
718719
const hash = txBody.hash() as unknown as HexBlob;
719720
const dRepPublicKey = await this.derivePublicKey(util.DREP_KEY_DERIVATION_PATH);
720721
const dRepKeyHashHex = (await Crypto.Ed25519PublicKey.fromHex(dRepPublicKey).hash()).hex();
722+
721723
const ledgerTxData = await toLedgerTx(body, {
722724
accountIndex: this.accountIndex,
723725
chainId: this.chainId,
724726
dRepKeyHashHex,
725727
knownAddresses,
726-
txInKeyPathMap
728+
txInKeyPathMap,
729+
useBabbageOutputs: txBody.hasBabbageOutput()
727730
});
728731

729732
const deviceConnection = await LedgerKeyAgent.checkDeviceConnection(

packages/hardware-ledger/src/transformers/txOut.ts

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -56,39 +56,11 @@ const getScriptHex = (output: Serialization.TransactionOutput): HexBlob | null =
5656
return scriptRef.toCbor();
5757
};
5858

59-
/**
60-
* There are currently two types of outputs supported by the ledger:
61-
*
62-
* legacy_transaction_output =
63-
* [ address
64-
* , amount : value
65-
* , ? datum_hash : $hash32
66-
* ]
67-
*
68-
* and
69-
*
70-
* post_alonzo_transaction_output =
71-
* { 0 : address
72-
* , 1 : value
73-
* , ? 2 : datum_option ; New; datum option
74-
* , ? 3 : script_ref ; New; script reference
75-
* }
76-
*
77-
* Legacy outputs are definite length arrays of three elements, however the new babbage outputs are definite length maps
78-
* of four elements.
79-
*
80-
* @param out The output to be verified.
81-
*/
82-
const isBabbage = (out: Serialization.TransactionOutput): boolean => {
83-
const reader = new Serialization.CborReader(out.toCbor());
84-
return reader.peekState() === Serialization.CborReaderState.StartMap;
85-
};
86-
8759
export const toTxOut: Transform<Cardano.TxOut, Ledger.TxOutput, LedgerTxTransformerContext> = (txOut, context) => {
8860
const output = Serialization.TransactionOutput.fromCore(txOut);
8961
const scriptHex = getScriptHex(output);
9062

91-
return isBabbage(output)
63+
return context?.useBabbageOutputs
9264
? {
9365
amount: txOut.value.coins,
9466
datum: txOut.datumHash ? toDatumHash(txOut.datumHash) : txOut.datum ? toInlineDatum(txOut.datum) : null,

packages/hardware-ledger/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ export type LedgerTxTransformerContext = {
2121
chainId: Cardano.ChainId;
2222
/** Non-hardened account in cip1852 */
2323
accountIndex: number;
24+
/** Whether to use Babbage output format or not. */
25+
useBabbageOutputs: boolean;
2426
} & SignTransactionContext;

packages/hardware-ledger/test/testData.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ export const CONTEXT_WITH_KNOWN_ADDRESSES: LedgerTxTransformerContext = {
357357
type: AddressType.Internal
358358
}
359359
],
360-
txInKeyPathMap: {}
360+
txInKeyPathMap: {},
361+
useBabbageOutputs: true
361362
};
362363

363364
export const CONTEXT_WITHOUT_KNOWN_ADDRESSES: LedgerTxTransformerContext = {
@@ -367,7 +368,8 @@ export const CONTEXT_WITHOUT_KNOWN_ADDRESSES: LedgerTxTransformerContext = {
367368
networkMagic: 999
368369
},
369370
knownAddresses: [],
370-
txInKeyPathMap: {}
371+
txInKeyPathMap: {},
372+
useBabbageOutputs: true
371373
};
372374

373375
export const votes = [

packages/hardware-ledger/test/transformers/certificates.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ const mockContext: LedgerTxTransformerContext = {
8585
txInKeyPathMap: createTxInKeyPathMapMock([
8686
createGroupedAddress(address1, ownRewardAccount, AddressType.External, 0, stakeKeyPath),
8787
createGroupedAddress(address2, ownRewardAccount, AddressType.External, 1, stakeKeyPath)
88-
])
88+
]),
89+
useBabbageOutputs: true
8990
};
9091

9192
const EXAMPLE_URL = 'https://example.com';

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ describe('tx', () => {
1515
txInKeyPathMap: {
1616
[TxInId(tx.body.inputs[0])]: paymentKeyPath,
1717
[TxInId(tx.body.collaterals![0])]: paymentKeyPath
18-
}
18+
},
19+
useBabbageOutputs: false
1920
})
2021
).toEqual({
2122
auxiliaryData: {
@@ -267,7 +268,8 @@ describe('tx', () => {
267268

268269
expect(
269270
await toLedgerTx(txBodyWithRegistrationCert, {
270-
...CONTEXT_WITH_KNOWN_ADDRESSES
271+
...CONTEXT_WITH_KNOWN_ADDRESSES,
272+
useBabbageOutputs: false
271273
})
272274
).toEqual({
273275
auxiliaryData: {

packages/hardware-ledger/test/transformers/txOut.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ describe('txOut', () => {
6464

6565
describe('toTxOut', () => {
6666
it('can map a simple txOut to third party address', async () => {
67-
const out = toTxOut(txOut, CONTEXT_WITH_KNOWN_ADDRESSES);
67+
const out = toTxOut(txOut, { ...CONTEXT_WITH_KNOWN_ADDRESSES, useBabbageOutputs: false });
6868

6969
expect(out).toEqual({
7070
amount: 10n,
@@ -114,7 +114,7 @@ describe('txOut', () => {
114114
});
115115

116116
it('can map a simple txOut to owned address', async () => {
117-
const out = toTxOut(txOutToOwnedAddress, CONTEXT_WITH_KNOWN_ADDRESSES);
117+
const out = toTxOut(txOutToOwnedAddress, { ...CONTEXT_WITH_KNOWN_ADDRESSES, useBabbageOutputs: false });
118118

119119
expect(out).toEqual({
120120
amount: 10n,

packages/hardware-trezor/src/TrezorKeyAgent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ export class TrezorKeyAgent extends KeyAgentBase {
251251
chainId: this.chainId,
252252
knownAddresses,
253253
tagCborSets: txBody.hasTaggedSets(),
254-
txInKeyPathMap
254+
txInKeyPathMap,
255+
useBabbageOutputs: txBody.hasBabbageOutput()
255256
});
256257

257258
const signingMode = TrezorKeyAgent.matchSigningMode(trezorTxData);

0 commit comments

Comments
 (0)