Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/core/src/Serialization/TransactionBody/TransactionBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,19 @@ export class TransactionBody {
return reader.peekState() === CborReaderState.Tag && reader.peekTag() === CborTag.Set;
}

/**
* Checks if the transaction body has Babbage outputs.
*
* @returns true if the transaction body has Babbage outputs, false otherwise.
*/
hasBabbageOutput(): boolean {
if (this.#outputs.length === 0) return false;

const reader = new CborReader(this.#outputs[0].toCbor());

return reader.peekState() === CborReaderState.StartMap;
}

/**
* Gets the size of the serialized map.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as Cardano from '../../../src/Cardano';
import * as Crypto from '@cardano-sdk/crypto';
import { HexBlob } from '@cardano-sdk/util';
import { TransactionBody, TxBodyCBOR, TxCBOR } from '../../../src/Serialization';
import { Transaction, TransactionBody, TxBodyCBOR, TxCBOR } from '../../../src/Serialization';
import { babbageTx } from '../testData';
import { mintTokenMap, params, txIn, txOut } from './testData';

Expand Down Expand Up @@ -273,6 +273,24 @@ describe('TransactionBody', () => {
expect(body.toCore()).toEqual(expectedConwayCore);
});

it('can encode identify transactions output format - Babbage outputs', () => {
const tx = Transaction.fromCbor(
TxCBOR(
'84a500818258207aa1264bcd0c06f34a49ed1dd7307a2bdec5a97bdeb546498759ad5b8ed42fd5010182a200583930195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d66195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d66011a00e4e1c0a2005839003d3246dc0c50ab3c74a8ffdd8068313ff99d341c461a8fe31f416d0a8fba06d60d71edc077cc5ebcb8ff82137afbce68df98271909332348011b000000025106a838021a00029309031a04a07bc6081a04a07a40a0f5f6'
)
);
expect(tx.body().hasBabbageOutput()).toBeTruthy();
});

it('can encode identify transactions output format - Legacy outputs', () => {
const tx = Transaction.fromCbor(
TxCBOR(
'84a500818258207aa1264bcd0c06f34a49ed1dd7307a2bdec5a97bdeb546498759ad5b8ed42fd501018282583930195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d66195bde3deacb613b7e9eb6280b14db4e353e475e96d19f3f7a5e2d661a00e4e1c0825839003d3246dc0c50ab3c74a8ffdd8068313ff99d341c461a8fe31f416d0a8fba06d60d71edc077cc5ebcb8ff82137afbce68df982719093323481b000000025106a838021a00029309031a04a07bc6081a04a07a40a0f5f6'
)
);
expect(tx.body().hasBabbageOutput()).toBeFalsy();
});

it('sorts withdrawals canonically', () => {
const body = TransactionBody.fromCbor(cbor);
const withdrawals = body.withdrawals();
Expand Down
5 changes: 4 additions & 1 deletion packages/hardware-ledger/src/LedgerKeyAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,15 +715,18 @@ export class LedgerKeyAgent extends KeyAgentBase {
): Promise<Cardano.Signatures> {
try {
const body = txBody.toCore();

const hash = txBody.hash() as unknown as HexBlob;
const dRepPublicKey = await this.derivePublicKey(util.DREP_KEY_DERIVATION_PATH);
const dRepKeyHashHex = (await Crypto.Ed25519PublicKey.fromHex(dRepPublicKey).hash()).hex();

const ledgerTxData = await toLedgerTx(body, {
accountIndex: this.accountIndex,
chainId: this.chainId,
dRepKeyHashHex,
knownAddresses,
txInKeyPathMap
txInKeyPathMap,
useBabbageOutputs: txBody.hasBabbageOutput()
});

const deviceConnection = await LedgerKeyAgent.checkDeviceConnection(
Expand Down
30 changes: 1 addition & 29 deletions packages/hardware-ledger/src/transformers/txOut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,39 +56,11 @@ const getScriptHex = (output: Serialization.TransactionOutput): HexBlob | null =
return scriptRef.toCbor();
};

/**
* There are currently two types of outputs supported by the ledger:
*
* legacy_transaction_output =
* [ address
* , amount : value
* , ? datum_hash : $hash32
* ]
*
* and
*
* post_alonzo_transaction_output =
* { 0 : address
* , 1 : value
* , ? 2 : datum_option ; New; datum option
* , ? 3 : script_ref ; New; script reference
* }
*
* Legacy outputs are definite length arrays of three elements, however the new babbage outputs are definite length maps
* of four elements.
*
* @param out The output to be verified.
*/
const isBabbage = (out: Serialization.TransactionOutput): boolean => {
const reader = new Serialization.CborReader(out.toCbor());
return reader.peekState() === Serialization.CborReaderState.StartMap;
};

export const toTxOut: Transform<Cardano.TxOut, Ledger.TxOutput, LedgerTxTransformerContext> = (txOut, context) => {
const output = Serialization.TransactionOutput.fromCore(txOut);
const scriptHex = getScriptHex(output);

return isBabbage(output)
return context?.useBabbageOutputs
? {
amount: txOut.value.coins,
datum: txOut.datumHash ? toDatumHash(txOut.datumHash) : txOut.datum ? toInlineDatum(txOut.datum) : null,
Expand Down
2 changes: 2 additions & 0 deletions packages/hardware-ledger/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ export type LedgerTxTransformerContext = {
chainId: Cardano.ChainId;
/** Non-hardened account in cip1852 */
accountIndex: number;
/** Whether to use Babbage output format or not. */
useBabbageOutputs: boolean;
} & SignTransactionContext;
6 changes: 4 additions & 2 deletions packages/hardware-ledger/test/testData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,8 @@ export const CONTEXT_WITH_KNOWN_ADDRESSES: LedgerTxTransformerContext = {
type: AddressType.Internal
}
],
txInKeyPathMap: {}
txInKeyPathMap: {},
useBabbageOutputs: true
};

export const CONTEXT_WITHOUT_KNOWN_ADDRESSES: LedgerTxTransformerContext = {
Expand All @@ -367,7 +368,8 @@ export const CONTEXT_WITHOUT_KNOWN_ADDRESSES: LedgerTxTransformerContext = {
networkMagic: 999
},
knownAddresses: [],
txInKeyPathMap: {}
txInKeyPathMap: {},
useBabbageOutputs: true
};

export const votes = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ const mockContext: LedgerTxTransformerContext = {
txInKeyPathMap: createTxInKeyPathMapMock([
createGroupedAddress(address1, ownRewardAccount, AddressType.External, 0, stakeKeyPath),
createGroupedAddress(address2, ownRewardAccount, AddressType.External, 1, stakeKeyPath)
])
]),
useBabbageOutputs: true
};

const EXAMPLE_URL = 'https://example.com';
Expand Down
6 changes: 4 additions & 2 deletions packages/hardware-ledger/test/transformers/tx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ describe('tx', () => {
txInKeyPathMap: {
[TxInId(tx.body.inputs[0])]: paymentKeyPath,
[TxInId(tx.body.collaterals![0])]: paymentKeyPath
}
},
useBabbageOutputs: false
})
).toEqual({
auxiliaryData: {
Expand Down Expand Up @@ -267,7 +268,8 @@ describe('tx', () => {

expect(
await toLedgerTx(txBodyWithRegistrationCert, {
...CONTEXT_WITH_KNOWN_ADDRESSES
...CONTEXT_WITH_KNOWN_ADDRESSES,
useBabbageOutputs: false
})
).toEqual({
auxiliaryData: {
Expand Down
4 changes: 2 additions & 2 deletions packages/hardware-ledger/test/transformers/txOut.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('txOut', () => {

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

expect(out).toEqual({
amount: 10n,
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('txOut', () => {
});

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

expect(out).toEqual({
amount: 10n,
Expand Down
3 changes: 2 additions & 1 deletion packages/hardware-trezor/src/TrezorKeyAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ export class TrezorKeyAgent extends KeyAgentBase {
chainId: this.chainId,
knownAddresses,
tagCborSets: txBody.hasTaggedSets(),
txInKeyPathMap
txInKeyPathMap,
useBabbageOutputs: txBody.hasBabbageOutput()
});

const signingMode = TrezorKeyAgent.matchSigningMode(trezorTxData);
Expand Down
30 changes: 1 addition & 29 deletions packages/hardware-trezor/src/transformers/txOut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,42 +40,14 @@ const getScriptHex = (output: Serialization.TransactionOutput): HexBlob | undefi
return scriptRef.toCbor();
};

/**
* There are currently two types of outputs supported by the ledger:
*
* legacy_transaction_output =
* [ address
* , amount : value
* , ? datum_hash : $hash32
* ]
*
* and
*
* post_alonzo_transaction_output =
* { 0 : address
* , 1 : value
* , ? 2 : datum_option ; New; datum option
* , ? 3 : script_ref ; New; script reference
* }
*
* Legacy outputs are definite length arrays of three elements, however the new babbage outputs are definite length maps
* of four elements.
*
* @param out The output to be verified.
*/
const isBabbage = (out: Serialization.TransactionOutput): boolean => {
const reader = new Serialization.CborReader(out.toCbor());
return reader.peekState() === Serialization.CborReaderState.StartMap;
};

const getInlineDatum = (datum: Cardano.PlutusData): string => Serialization.PlutusData.fromCore(datum).toCbor();

export const toTxOut: Transform<Cardano.TxOut, Trezor.CardanoOutput, TrezorTxTransformerContext> = (txOut, context) => {
const destination = toDestination(txOut, context);
const output = Serialization.TransactionOutput.fromCore(txOut);
const scriptHex = getScriptHex(output);

return isBabbage(output)
return context?.useBabbageOutputs
? {
...destination,
amount: txOut.value.coins.toString(),
Expand Down
2 changes: 2 additions & 0 deletions packages/hardware-trezor/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export type TrezorTxTransformerContext = {
accountIndex: number;
/** Whether sets should be encoded as tagged set in CBOR */
tagCborSets: boolean;
/** Whether to use Babbage output format or not. */
useBabbageOutputs: boolean;
} & SignTransactionContext;

export type TrezorTxOutputDestination =
Expand Down
9 changes: 6 additions & 3 deletions packages/hardware-trezor/test/testData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ export const contextWithKnownAddresses: TrezorTxTransformerContext = {
},
knownAddresses: [knownAddress],
tagCborSets: false,
txInKeyPathMap: {}
txInKeyPathMap: {},
useBabbageOutputs: false
};

export const contextWithKnownAddressesWithoutStakingCredentials: TrezorTxTransformerContext = {
Expand All @@ -182,7 +183,8 @@ export const contextWithKnownAddressesWithoutStakingCredentials: TrezorTxTransfo
},
knownAddresses: [knownAddressWithoutStakingPath],
tagCborSets: false,
txInKeyPathMap: {}
txInKeyPathMap: {},
useBabbageOutputs: false
};

export const contextWithoutKnownAddresses: TrezorTxTransformerContext = {
Expand All @@ -193,7 +195,8 @@ export const contextWithoutKnownAddresses: TrezorTxTransformerContext = {
},
knownAddresses: [],
tagCborSets: false,
txInKeyPathMap: {}
txInKeyPathMap: {},
useBabbageOutputs: false
};

export const coreWithdrawalWithKeyHashCredential = {
Expand Down
8 changes: 5 additions & 3 deletions packages/hardware-trezor/test/transformers/tx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ describe('tx', () => {
...contextWithKnownAddresses,
txInKeyPathMap: {
[TxInId(babbageTxBodyWithScripts.inputs[0])]: knownAddressPaymentKeyPath
}
},
useBabbageOutputs: true
})
).toEqual({
additionalWitnessRequests: [
Expand Down Expand Up @@ -327,7 +328,8 @@ describe('tx', () => {
txInKeyPathMap: {
[TxInId(plutusTxWithBabbage.inputs[0])]: knownAddressPaymentKeyPath,
[TxInId(plutusTxWithBabbage.collaterals[0])]: knownAddressPaymentKeyPath
}
},
useBabbageOutputs: true
})
).toEqual({
additionalWitnessRequests: [
Expand Down Expand Up @@ -361,7 +363,7 @@ describe('tx', () => {
address:
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp',
amount: '10',
format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY
format: Trezor.PROTO.CardanoTxOutputSerializationFormat.MAP_BABBAGE
},
fee: '10',
inputs: [
Expand Down
38 changes: 27 additions & 11 deletions packages/hardware-trezor/test/transformers/txOut.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,16 @@ describe('txOut', () => {
});

it('can map a set of transaction outputs with both output formats', async () => {
const txOuts = mapTxOuts(
[txOutWithDatumHashAndOwnedAddress, txOutWithReferenceScriptAndDatumHash],
contextWithKnownAddresses
);
const legacyTxOuts = mapTxOuts([txOutWithDatumHashAndOwnedAddress], contextWithKnownAddresses);

expect(txOuts.length).toEqual(2);
const babbageTxOuts = mapTxOuts([txOutWithReferenceScriptAndDatumHash], {
...contextWithKnownAddresses,
useBabbageOutputs: true
});

expect(txOuts).toEqual([
expect(legacyTxOuts.length).toEqual(1);

expect(legacyTxOuts).toEqual([
{
addressParameters: {
addressType: Trezor.PROTO.CardanoAddressType.BASE,
Expand All @@ -173,7 +175,12 @@ describe('txOut', () => {
amount: '10',
datumHash: '0f3abbc8fc19c2e61bab6059bf8a466e6e754833a08a62a6c56fe0e78f19d9d5',
format: Trezor.PROTO.CardanoTxOutputSerializationFormat.ARRAY_LEGACY
},
}
]);

expect(babbageTxOuts.length).toEqual(1);

expect(babbageTxOuts).toEqual([
{
addressParameters: {
addressType: Trezor.PROTO.CardanoAddressType.BASE,
Expand Down Expand Up @@ -332,7 +339,7 @@ describe('txOut', () => {
});

it('can map simple transaction with inline datum', async () => {
const out = toTxOut(txOutWithInlineDatum, contextWithKnownAddresses);
const out = toTxOut(txOutWithInlineDatum, { ...contextWithKnownAddresses, useBabbageOutputs: true });

expect(out).toEqual({
address:
Expand All @@ -344,7 +351,10 @@ describe('txOut', () => {
});

it('can map simple transaction with inline datum to owned address', async () => {
const out = toTxOut(txOutWithInlineDatumAndOwnedAddress, contextWithKnownAddresses);
const out = toTxOut(txOutWithInlineDatumAndOwnedAddress, {
...contextWithKnownAddresses,
useBabbageOutputs: true
});

expect(out).toEqual({
addressParameters: {
Expand All @@ -359,7 +369,10 @@ describe('txOut', () => {
});

it('can map a simple transaction output with reference script and datum hash', async () => {
const out = toTxOut(txOutWithReferenceScriptAndDatumHash, contextWithKnownAddresses);
const out = toTxOut(txOutWithReferenceScriptAndDatumHash, {
...contextWithKnownAddresses,
useBabbageOutputs: true
});
expect(out).toEqual({
addressParameters: {
addressType: Trezor.PROTO.CardanoAddressType.BASE,
Expand All @@ -374,7 +387,10 @@ describe('txOut', () => {
});

it('can map a simple transaction output with reference script and inline datum', async () => {
const out = toTxOut(txOutWithReferenceScriptAndInlineDatum, contextWithKnownAddresses);
const out = toTxOut(txOutWithReferenceScriptAndInlineDatum, {
...contextWithKnownAddresses,
useBabbageOutputs: true
});
expect(out).toEqual({
addressParameters: {
addressType: Trezor.PROTO.CardanoAddressType.BASE,
Expand Down
Loading
Loading