Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

app split (XS app) #195

Closed
wants to merge 2 commits into from
Closed
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
24 changes: 24 additions & 0 deletions src/interactions/deriveAddress.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import {DeviceVersionUnsupported} from '../errors'
import {getVersionString} from '../utils'
import type {ParsedAddressParams, Version} from '../types/internal'
import {AddressType} from '../types/public'
import type {DerivedAddress} from '../types/public'
import {INS} from './common/ins'
import type {Interaction, SendParams} from './common/types'
import {ensureLedgerAppVersionCompatible, getCompatibility} from './getVersion'
import {serializeAddressParams} from './serialization/addressParams'

const send = (params: {
Expand All @@ -11,10 +15,30 @@ const send = (params: {
expectedResponseLength?: number
}): SendParams => ({ins: INS.DERIVE_ADDRESS, ...params})

export function ensureAddressDerivationSupportedByAppVersion(
version: Version,
addressParams: ParsedAddressParams,
): void {
ensureLedgerAppVersionCompatible(version)

if (
addressParams.type === AddressType.BYRON &&
!getCompatibility(version).supportsByronAddressDerivation
) {
throw new DeviceVersionUnsupported(
`Byron address parameters not supported by Ledger app version ${getVersionString(
version,
)}.`,
)
}
}

export function* deriveAddress(
version: Version,
addressParams: ParsedAddressParams,
): Interaction<DerivedAddress> {
ensureAddressDerivationSupportedByAppVersion(version, addressParams)

const P1_RETURN = 0x01
const P2_UNUSED = 0x00

Expand Down
13 changes: 10 additions & 3 deletions src/interactions/getVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ export function* getVersion(): Interaction<Version> {

const FLAG_IS_DEBUG = 1
// const FLAG_IS_HEADLESS = 2;
const FLAG_IS_APP_XS = 4

const flags = {
// eslint-disable-next-line no-bitwise
isDebug: (flags_value & FLAG_IS_DEBUG) === FLAG_IS_DEBUG,
// eslint-disable-next-line no-bitwise
isAppXS: (flags_value & FLAG_IS_APP_XS) === FLAG_IS_APP_XS,
}
return {major, minor, patch, flags}
}
Expand Down Expand Up @@ -83,16 +86,20 @@ export function getCompatibility(version: Version): DeviceCompatibility {
isLedgerAppVersionAtLeast(version, 6, 0) &&
isLedgerAppVersionAtMost(version, 6, Infinity)

const isAppXS = version.flags.isAppXS

return {
isCompatible: v2_2,
recommendedVersion: v2_2 ? null : '6.0',
supportsByronAddressDerivation: v2_2 && !isAppXS,
supportsMary: v2_2,
supportsCatalystRegistration: v2_3, // CIP-15
supportsCIP36: v6_0,
supportsZeroTtl: v2_3,
supportsPoolRegistrationAsOperator: v2_4,
supportsPoolRetirement: v2_4,
supportsNativeScriptHashDerivation: v3_0,
supportsPoolRegistrationAsOwner: v2_2 && !isAppXS,
supportsPoolRegistrationAsOperator: v2_4 && !isAppXS,
supportsPoolRetirement: v2_4 && !isAppXS,
supportsNativeScriptHashDerivation: v3_0 && !isAppXS,
supportsMultisigTransaction: v3_0,
supportsMint: v3_0,
supportsAlonzo: v4_0,
Expand Down
7 changes: 6 additions & 1 deletion src/interactions/serialization/txInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ export function serializeTxInit(
numWitnesses: number,
version: Version,
) {
const mintBuffer = getCompatibility(version).supportsMint
const appAwareOfMint =
getCompatibility(version).supportsMint || version.flags.isAppXS
janmazak marked this conversation as resolved.
Show resolved Hide resolved
// even though XS app doesn't support minting, it does expect the flag value
// (we want to keep the APDU serialization uniform)
const mintBuffer = appAwareOfMint
? serializeOptionFlag(tx.mint != null)
: Buffer.from([])

const scriptDataHashBuffer = getCompatibility(version).supportsAlonzo
? serializeOptionFlag(tx.scriptDataHashHex != null)
: Buffer.from([])
Expand Down
5 changes: 3 additions & 2 deletions src/interactions/showAddress.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {ParsedAddressParams, Version} from '../types/internal'
import {INS} from './common/ins'
import type {Interaction, SendParams} from './common/types'
import {ensureLedgerAppVersionCompatible} from './getVersion'
import {serializeAddressParams} from './serialization/addressParams'
import {ensureAddressDerivationSupportedByAppVersion} from './deriveAddress'

const send = (params: {
p1: number
Expand All @@ -15,7 +15,8 @@ export function* showAddress(
version: Version,
addressParams: ParsedAddressParams,
): Interaction<void> {
ensureLedgerAppVersionCompatible(version)
ensureAddressDerivationSupportedByAppVersion(version, addressParams)

const P1_DISPLAY = 0x02
const P2_UNUSED = 0x00

Expand Down
45 changes: 45 additions & 0 deletions src/interactions/signTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,17 @@ function ensureRequestSupportedByAppVersion(
): void {
// signing modes

if (
request.signingMode === TransactionSigningMode.POOL_REGISTRATION_AS_OWNER &&
!getCompatibility(version).supportsPoolRegistrationAsOwner
) {
throw new DeviceVersionUnsupported(
`Pool registration as owner not supported by Ledger app version ${getVersionString(
version,
)}.`,
)
}

if (
request.signingMode ===
TransactionSigningMode.POOL_REGISTRATION_AS_OPERATOR &&
Expand Down Expand Up @@ -1000,6 +1011,26 @@ function ensureRequestSupportedByAppVersion(

// transaction elements

const isOutputByron = (o: ParsedOutput | null) =>
o != null &&
o.destination.type === TxOutputDestinationType.DEVICE_OWNED &&
o.destination.addressParams.type === AddressType.BYRON

const hasByronAddressParam =
request.tx.outputs.some(isOutputByron) ||
isOutputByron(request.tx.collateralOutput)
janmazak marked this conversation as resolved.
Show resolved Hide resolved
// Byron collateral outputs forbidden by the security policy
if (
hasByronAddressParam &&
!getCompatibility(version).supportsByronAddressDerivation
) {
throw new DeviceVersionUnsupported(
`Byron address parameters not supported by Ledger app version ${getVersionString(
version,
)}.`,
)
}

if (
hasScriptHashInAddressParams(request.tx) &&
!getCompatibility(version).supportsMultisigTransaction
Expand Down Expand Up @@ -1064,6 +1095,20 @@ function ensureRequestSupportedByAppVersion(
)
}

const hasPoolRegistration = request.tx.certificates.some(
(c) => c.type === CertificateType.STAKE_POOL_REGISTRATION,
)
const supportsPoolRegistration =
getCompatibility(version).supportsPoolRegistrationAsOwner ||
getCompatibility(version).supportsPoolRegistrationAsOperator
if (hasPoolRegistration && !supportsPoolRegistration) {
throw new DeviceVersionUnsupported(
`Pool registration certificate not supported by Ledger app version ${getVersionString(
version,
)}.`,
)
}

davidmisiak marked this conversation as resolved.
Show resolved Hide resolved
const hasPoolRetirement = request.tx.certificates.some(
(c) => c.type === CertificateType.STAKE_POOL_RETIREMENT,
)
Expand Down
11 changes: 10 additions & 1 deletion src/types/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,7 @@ export type Withdrawal = {
*/
export type Flags = {
isDebug: boolean
isAppXS: boolean
}

/**
Expand Down Expand Up @@ -910,6 +911,10 @@ export type DeviceCompatibility = {
* Clients of SDK should check whether this is null and if not, urge users to upgrade.
*/
recommendedVersion: string | null
/**
* Whether we support Byron address parameters in transaction outputs
*/
supportsByronAddressDerivation: boolean
/**
* Whether we support Mary features
*/
Expand All @@ -928,7 +933,11 @@ export type DeviceCompatibility = {
*/
supportsZeroTtl: boolean
/**
* Whether we support operational certificate signing
* Whether we support pool registration certificate signing by pool owners
*/
supportsPoolRegistrationAsOwner: boolean
/**
* Whether we support operational and pool registration certificate signing by pool operators
*/
supportsPoolRegistrationAsOperator: boolean
/**
Expand Down
3 changes: 2 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ export default {
}

export function getVersionString(version: Version): string {
return `${version.major}.${version.minor}.${version.patch}`
const xs = version.flags.isAppXS ? ' XS' : ''
return `${version.major}.${version.minor}.${version.patch}${xs}`
}
6 changes: 5 additions & 1 deletion test/integration/__fixtures__/deriveAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ type RejectTestCase = {
addressParams: DeviceOwnedAddress
errCls: new (...args: any[]) => ErrorBase
errMsg: string
unsupportedInAppXS?: boolean
}

const rejectTestCaseBase = {
Expand All @@ -616,7 +617,7 @@ const rejectTestCaseBase = {
errMsg: DeviceStatusMessages[DeviceStatusCodes.ERR_REJECTED_BY_POLICY],
}

export const RejectTestCases: RejectTestCase[] = [
export const rejectTestCases: RejectTestCase[] = [
{
testName: 'path too short',
...rejectTestCaseBase,
Expand All @@ -626,6 +627,7 @@ export const RejectTestCases: RejectTestCase[] = [
spendingPath: str_to_path("44'/1815'/1'"),
},
},
unsupportedInAppXS: true,
},
{
testName: 'invalid path',
Expand All @@ -636,6 +638,7 @@ export const RejectTestCases: RejectTestCase[] = [
spendingPath: str_to_path("44'/1815'/1'/5/10'"),
},
},
unsupportedInAppXS: true,
},
{
testName: 'Byron with Shelley path',
Expand All @@ -646,6 +649,7 @@ export const RejectTestCases: RejectTestCase[] = [
spendingPath: str_to_path("1852'/1815'/1'/0/10"),
},
},
unsupportedInAppXS: true,
},
{
testName: 'base key/key with Byron spending path',
Expand Down
3 changes: 3 additions & 0 deletions test/integration/__fixtures__/signTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type SignTxTestCase = {
txBody?: string
txAuxiliaryData?: string
expectedResult: SignedTransactionData
unsupportedInAppXS?: boolean
}

export const testsByron: SignTxTestCase[] = [
Expand Down Expand Up @@ -522,6 +523,7 @@ export const testsShelleyWithCertificates: SignTxTestCase[] = [
},
{
testName: 'Sign tx with pool retirement combined with stake registration',
unsupportedInAppXS: true,
tx: {
...shelleyBase,
inputs: [
Expand Down Expand Up @@ -576,6 +578,7 @@ export const testsShelleyWithCertificates: SignTxTestCase[] = [
},
{
testName: 'Sign tx with pool retirement combined with stake deregistration',
unsupportedInAppXS: true,
tx: {
...shelleyBase,
inputs: [
Expand Down
11 changes: 11 additions & 0 deletions test/integration/__fixtures__/signTxPoolRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
{
testName:
'Witness valid multiple mixed owners all relays pool registration',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [certificates.poolRegistrationMixedOwnersAllRelays],
Expand All @@ -392,6 +393,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
},
{
testName: 'Witness valid single path owner ipv4 relay pool registration',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [certificates.poolRegistrationDefault],
Expand All @@ -416,6 +418,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
{
testName:
'Witness valid multiple mixed owners ipv4 relay pool registration',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [certificates.poolRegistrationMixedOwners],
Expand All @@ -440,6 +443,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
{
testName:
'Witness valid multiple mixed owners mixed ipv4, single host relays pool registration',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [
Expand All @@ -466,6 +470,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
{
testName:
'Witness valid multiple mixed owners mixed ipv4 ipv6 relays pool registration',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [certificates.poolRegistrationMixedOwnersIpv4Ipv6Relays],
Expand All @@ -489,6 +494,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
},
{
testName: 'Witness valid single path owner no relays pool registration',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [certificates.poolRegistrationNoRelays],
Expand All @@ -513,6 +519,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
{
// works as a private pool not visible in yoroi, daedalus, etc.
testName: 'Witness pool registration with no metadata',
unsupportedInAppXS: true,
tx: {
...txBase,
certificates: [certificates.poolRegistrationNoMetadata],
Expand All @@ -536,6 +543,7 @@ export const poolRegistrationOwnerTestCases: SignTxTestCase[] = [
},
{
testName: 'Witness pool registration without outputs',
unsupportedInAppXS: true,
tx: {
...txBase,
outputs: [],
Expand Down Expand Up @@ -564,6 +572,7 @@ export const poolRegistrationOperatorTestCases: SignTxTestCase[] = [
{
testName:
'Witness pool registration as operator with no owners and no relays',
unsupportedInAppXS: true,
tx: {
...txBase,
inputs: [inputs.utxoWithPath0],
Expand Down Expand Up @@ -594,6 +603,7 @@ export const poolRegistrationOperatorTestCases: SignTxTestCase[] = [
{
testName:
'Witness pool registration as operator with one owner and no relays',
unsupportedInAppXS: true,
tx: {
...txBase,
inputs: [inputs.utxoWithPath0],
Expand Down Expand Up @@ -626,6 +636,7 @@ export const poolRegistrationOperatorTestCases: SignTxTestCase[] = [
{
testName:
'Witness pool registration as operator with multiple owners and all relays',
unsupportedInAppXS: true,
tx: {
...txBase,
inputs: [inputs.utxoWithPath0],
Expand Down
Loading
Loading