Skip to content

Commit

Permalink
feature: add cbor set tag 258
Browse files Browse the repository at this point in the history
  • Loading branch information
janmazak committed Jan 9, 2024
1 parent 69761f1 commit 64520ff
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 10 deletions.
24 changes: 24 additions & 0 deletions src/interactions/serialization/txInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import type {
Uint8_t,
Uint32_t,
Version,
ParsedTransactionOptions,
Uint64_str,
} from '../../types/internal'
import {TransactionSigningMode} from '../../types/internal'
import {assert} from '../../utils/assert'
import {
serializeOptionFlag,
uint8_to_buf,
uint32_to_buf,
uint64_to_buf,
} from '../../utils/serialize'
import {getCompatibility} from '../getVersion'

Expand All @@ -27,12 +30,32 @@ const _serializeSigningMode = (mode: TransactionSigningMode): Buffer => {
return uint8_to_buf(value)
}

const enum OptionFlags {
TAG_CBOR_SETS = 1,
}

function serializeTxOptions(options: ParsedTransactionOptions): Buffer {
// we reserve 64 bits for options in the APDU for future use
// the code below would need to be changed if a typescript number
// was not enough to fit all the future flags
let optionFlags = 0
if (options.tagCborSets) {
optionFlags += OptionFlags.TAG_CBOR_SETS
}
return uint64_to_buf(optionFlags.toString() as Uint64_str)
}

export function serializeTxInit(
tx: ParsedTransaction,
signingMode: TransactionSigningMode,
numWitnesses: number,
options: ParsedTransactionOptions,
version: Version,
) {
const optionsBuffer = getCompatibility(version).supportsConway
? serializeTxOptions(options)
: Buffer.from([])

const appAwareOfMint =
getCompatibility(version).supportsMint || version.flags.isAppXS
// even if XS app doesn't support minting, it does expect the flag value
Expand Down Expand Up @@ -82,6 +105,7 @@ export function serializeTxInit(
: Buffer.from([])

return Buffer.concat([
optionsBuffer,
uint8_to_buf(tx.network.networkId),
uint32_to_buf(tx.network.protocolMagic),
serializeOptionFlag(tx.ttl != null),
Expand Down
12 changes: 10 additions & 2 deletions src/interactions/signTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
ParsedRequiredSigner,
ParsedSigningRequest,
ParsedTransaction,
ParsedTransactionOptions,
ParsedTxAuxiliaryData,
ParsedVoterVotes,
ParsedWithdrawal,
Expand Down Expand Up @@ -133,6 +134,7 @@ function* signTx_init(
tx: ParsedTransaction,
signingMode: TransactionSigningMode,
witnessPaths: ValidBIP32Path[],
options: ParsedTransactionOptions,
version: Version,
): Interaction<void> {
const enum P2 {
Expand All @@ -141,7 +143,13 @@ function* signTx_init(
yield send({
p1: P1.STAGE_INIT,
p2: P2.UNUSED,
data: serializeTxInit(tx, signingMode, witnessPaths.length, version),
data: serializeTxInit(
tx,
signingMode,
witnessPaths.length,
options,
version,
),
expectedResponseLength: 0,
})
}
Expand Down Expand Up @@ -1444,7 +1452,7 @@ export function* signTransaction(
const witnessPaths = gatherWitnessPaths(request)

// init
yield* signTx_init(tx, signingMode, witnessPaths, version)
yield* signTx_init(tx, signingMode, witnessPaths, request.options, version)

// auxiliary data
let auxiliaryDataSupplement = null
Expand Down
23 changes: 20 additions & 3 deletions src/parsing/transaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {InvalidData} from '../errors'
import {InvalidDataReason} from '../errors/invalidDataReason'
import type {ParsedSigningRequest, ParsedTransaction} from '../types/internal'
import type {
ParsedSigningRequest,
ParsedTransaction,
ParsedTransactionOptions,
} from '../types/internal'
import {
ParsedCertificate,
ParsedInput,
Expand All @@ -17,7 +21,11 @@ import {
CredentialType,
TX_HASH_LENGTH,
} from '../types/internal'
import type {SignTransactionRequest, Transaction} from '../types/public'
import type {
SignTransactionRequest,
Transaction,
TransactionOptions,
} from '../types/public'
import {
Certificate,
RequiredSigner,
Expand Down Expand Up @@ -392,11 +400,20 @@ export function parseTransaction(tx: Transaction): ParsedTransaction {
}
}

function parseTxOptions(
options: TransactionOptions | undefined,
): ParsedTransactionOptions {
return {
tagCborSets: options?.tagCborSets || false,
}
}

export function parseSignTransactionRequest(
request: SignTransactionRequest,
): ParsedSigningRequest {
const tx = parseTransaction(request.tx)
const signingMode = parseSigningMode(request.signingMode)
const options = parseTxOptions(request.options)

validate(
isArray(request.additionalWitnessPaths ?? []),
Expand Down Expand Up @@ -861,5 +878,5 @@ export function parseSignTransactionRequest(
unreachable(signingMode)
}

return {tx, signingMode, additionalWitnessPaths}
return {tx, signingMode, additionalWitnessPaths, options}
}
5 changes: 5 additions & 0 deletions src/types/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,15 @@ export type ParsedTransaction = {
donation: Uint64_str | null
}

export type ParsedTransactionOptions = {
tagCborSets: boolean
}

export type ParsedSigningRequest = {
tx: ParsedTransaction
signingMode: TransactionSigningMode
additionalWitnessPaths: ValidBIP32Path[]
options: ParsedTransactionOptions
}

export type ScriptDataHash = FixLenHexString<typeof SCRIPT_DATA_HASH_LENGTH>
Expand Down
21 changes: 16 additions & 5 deletions src/types/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1806,29 +1806,40 @@ export enum TransactionSigningMode {
PLUTUS_TRANSACTION = 'plutus_transaction',
}

export type TransactionOptions = {
/**
* If true, serialize transactions with 258 tags for all sets (optional since Conway).
* If false or not given, do not use the tags.
*/
tagCborSets?: boolean
}

/**
* Transaction signing request.
* This represents the transaction user wants to sign.
* Due to certain Ledger limitation, we also require user to specify a "mode" in which he/she wants to sign the transaction.
* @category Basic types
*/
export type SignTransactionRequest = {
/**
* Transaction to be signed
*/
tx: Transaction
/**
* Mode in which we want to sign the transaction.
* Ledger has certain limitations (see [[TransactionSigningMode]] in detail) due to which
* it cannot sign arbitrary combination of all transaction features.
* The mode specifies which use-case the user want to use and triggers additional validation on `tx` field.
*/
signingMode: TransactionSigningMode
/**
* Transaction to be signed
*/
tx: Transaction

/**
* Additional witness paths that are not gathered from the transaction body, eg. mint witnesses
*/
additionalWitnessPaths?: BIP32Path[]
/**
* Additional options used in transaction processing (e.g. details of serialization).
*/
options?: TransactionOptions
}

/**
Expand Down
32 changes: 32 additions & 0 deletions test/integration/__fixtures__/signTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
DRepParamsType,
VoterType,
VoteOption,
TransactionOptions,
} from '../../../src/types/public'
import {str_to_path} from '../../../src/utils/address'
import {
Expand All @@ -25,6 +26,7 @@ export type SignTxTestCase = {
tx: Transaction
signingMode: TransactionSigningMode
additionalWitnessPaths?: BIP32Path[]
options?: TransactionOptions
txBody?: string
txAuxiliaryData?: string
expectedResult: SignedTransactionData
Expand Down Expand Up @@ -124,6 +126,9 @@ export const testsShelleyNoCertificates: SignTxTestCase[] = [
},
signingMode: TransactionSigningMode.ORDINARY_TRANSACTION,
additionalWitnessPaths: undefined,
options: {
tagCborSets: false,
},
txBody:
'a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018002182a030a',
expectedResult: {
Expand All @@ -139,6 +144,33 @@ export const testsShelleyNoCertificates: SignTxTestCase[] = [
auxiliaryDataSupplement: null,
},
},
{
testName: 'Sign tx with 258 tag on inputs',
tx: {
...mainnetFeeTtl,
inputs: [inputs.utxoShelley],
outputs: [],
},
signingMode: TransactionSigningMode.ORDINARY_TRANSACTION,
additionalWitnessPaths: undefined,
options: {
tagCborSets: true,
},
txBody:
'a400d90102818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018002182a030a',
expectedResult: {
txHashHex:
'063d3a3670a43699a2648df93eedc1f93e8fda898ab79d3a795142a4ad573b7b',
witnesses: [
{
path: str_to_path("1852'/1815'/0'/0/0"),
witnessSignatureHex:
'b842908ce71f3ad1e1a1e2261c3bfdbfdb48c3fe58484c3e0521588e94e48fdb001f30908b0cd041e6c1b9d9400739ea52d0ca7289b3d807d26d06d73961f609',
},
],
auxiliaryDataSupplement: null,
},
},
{
testName: 'Sign tx without change address',
tx: {
Expand Down
2 changes: 2 additions & 0 deletions test/test_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export function describeSignTxPositiveTest(name: string, tests: any[]) {
tx,
signingMode,
additionalWitnessPaths,
options,
txBody,
expectedResult,
unsupportedInAppXS,
Expand All @@ -233,6 +234,7 @@ export function describeSignTxPositiveTest(name: string, tests: any[]) {
tx,
signingMode,
additionalWitnessPaths,
options,
})

if (isAppXS && unsupportedInAppXS) {
Expand Down

0 comments on commit 64520ff

Please sign in to comment.