Skip to content
Open
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
40 changes: 23 additions & 17 deletions scripts/fix-trezor-firmware.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
const fs = require('fs');
const path = require('path');
const fs = require('fs')
const path = require('path')

// Fix 1: @trezor/connect beta version missing firmware module
const firmwarePath = path.join(__dirname, '../node_modules/@trezor/connect-common/files/firmware');
const firmwareIndexPath = path.join(firmwarePath, 'index.js');
const firmwarePath = path.join(
__dirname,
'../node_modules/@trezor/connect-common/files/firmware',
)
const firmwareIndexPath = path.join(firmwarePath, 'index.js')

// Only create the firmware fix if the file doesn't exist
if (!fs.existsSync(firmwareIndexPath)) {
Expand Down Expand Up @@ -41,33 +44,36 @@ deviceModels.forEach(deviceModel => {
});
});

module.exports = { firmwareAssets };`;
module.exports = { firmwareAssets };`

try {
fs.writeFileSync(firmwareIndexPath, indexContent);
console.log('✓ Fixed @trezor/connect-common firmware module');
fs.writeFileSync(firmwareIndexPath, indexContent)
console.log('✓ Fixed @trezor/connect-common firmware module')
} catch (error) {
console.warn('⚠ Could not create firmware index.js:', error.message);
console.warn('⚠ Could not create firmware index.js:', error.message)
}
}

// Fix 2: @trezor/connect beta version trying to import .ts files instead of .js
const methodPath = path.join(__dirname, '../node_modules/@trezor/connect/lib/core/method.js');
const methodPath = path.join(
__dirname,
'../node_modules/@trezor/connect/lib/core/method.js',
)

if (fs.existsSync(methodPath)) {
try {
let methodContent = fs.readFileSync(methodPath, 'utf8');
let methodContent = fs.readFileSync(methodPath, 'utf8')

// Replace .ts extension with .js in the dynamic import
const original = `\`../api/\${methodModule}/api/index.ts\``;
const fixed = `\`../api/\${methodModule}/api/index.js\``;
const original = `\`../api/\${methodModule}/api/index.ts\``
const fixed = `\`../api/\${methodModule}/api/index.js\``

if (methodContent.includes(original)) {
methodContent = methodContent.replace(original, fixed);
fs.writeFileSync(methodPath, methodContent);
console.log('✓ Fixed @trezor/connect method.js TypeScript import');
methodContent = methodContent.replace(original, fixed)
fs.writeFileSync(methodPath, methodContent)
console.log('✓ Fixed @trezor/connect method.js TypeScript import')
}
} catch (error) {
console.warn('⚠ Could not fix method.js:', error.message);
console.warn('⚠ Could not fix method.js:', error.message)
}
}
}
13 changes: 7 additions & 6 deletions src/command-parser/argTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ export type ParsedDeviceVersionArguments = {
command: CommandType.DEVICE_VERSION
}

// exactly one of paymentPath vs. paymentScriptHash and stakingPath vs. stakingScriptHash
// should be present (the result of parse() complies with this)
// For base addresses: exactly one of paymentPath vs. paymentScriptHash AND one of stakingPath vs. stakingScriptHash
// For enterprise addresses: exactly one of paymentPath vs. paymentScriptHash (no staking credentials)
// For reward addresses: exactly one of stakingPath vs. stakingScriptHash (no payment credentials)
export type ParsedShowAddressArguments = {
command: CommandType.SHOW_ADDRESS
paymentPath: BIP32Path
paymentScriptHash: string
stakingPath: BIP32Path
stakingScriptHash: string
paymentPath?: BIP32Path
paymentScriptHash?: string
stakingPath?: BIP32Path
stakingScriptHash?: string
address: HumanAddress
derivationType?: DerivationType
}
Expand Down
4 changes: 2 additions & 2 deletions src/command-parser/parserConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export const parserConfig: ParserConfig = {

'show': {
// hw-specific subpath
'_mutually-exclusive-group-required-payment': {
'_mutually-exclusive-group-payment': {
'--payment-path': {
dest: 'paymentPath',
type: (path: string) => parseBIP32Path(path),
Expand All @@ -252,7 +252,7 @@ export const parserConfig: ParserConfig = {
help: 'Payment derivation script hash in hex format.',
},
},
'_mutually-exclusive-group-required-staking': {
'_mutually-exclusive-group-staking': {
'--staking-path': {
dest: 'stakingPath',
type: (path: string) => parseBIP32Path(path),
Expand Down
57 changes: 49 additions & 8 deletions src/commandExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ import {
determineSigningMode,
getTxBodyHash,
pathEquals,
getAddressAttributes,
} from './crypto-providers/util'
import {Errors} from './errors'
import {parseOpCertIssueCounterFile} from './command-parser/parsers'
import {CIP36_VOTING_PURPOSE_CATALYST} from './constants'
import {validateWitnessing} from './crypto-providers/witnessingValidation'
import {validateTxBeforeWitnessing} from './transaction/transactionValidation'
import {Cbor, CVoteDelegation} from './basicTypes'
import {AddressType, Cbor, CVoteDelegation} from './basicTypes'
import {KeystoneCryptoProvider} from './crypto-providers/keystoneCryptoProvider'

const promiseTimeout = <T>(promise: Promise<T>, ms: number): Promise<T> => {
Expand All @@ -67,15 +68,16 @@ const getCryptoProvider = async (): Promise<CryptoProvider> => {
const keystonePromise = async () =>
await KeystoneCryptoProvider(await TransportNodeUSB.connect())
try {
const results = await
Promise.allSettled([
promiseTimeout(ledgerPromise(), 5000),
promiseTimeout(trezorPromise(), 5000),
promiseTimeout(keystonePromise(), 5000),
])
const results = await Promise.allSettled([
promiseTimeout(ledgerPromise(), 5000),
promiseTimeout(trezorPromise(), 5000),
promiseTimeout(keystonePromise(), 5000),
])

// Find the first successful result
const successfulResult = results.find(result => result.status === 'fulfilled')
const successfulResult = results.find(
(result) => result.status === 'fulfilled',
)
if (successfulResult && successfulResult.status === 'fulfilled') {
return successfulResult.value
}
Expand All @@ -94,7 +96,46 @@ const CommandExecutor = async () => {
// eslint-disable-next-line no-console
console.log(await cryptoProvider.getVersion())

const validateShowAddressArgs = (args: ParsedShowAddressArguments): void => {
const hasPaymentCredential =
args.paymentPath != null || args.paymentScriptHash != null
const hasStakingCredential =
args.stakingPath != null || args.stakingScriptHash != null

if (!hasPaymentCredential && !hasStakingCredential) {
throw Error(Errors.InvalidAddressParametersProvidedError)
}

const {addressType} = getAddressAttributes(args.address)

switch (addressType) {
case AddressType.BASE_PAYMENT_KEY_STAKE_KEY:
case AddressType.BASE_PAYMENT_SCRIPT_STAKE_KEY:
case AddressType.BASE_PAYMENT_KEY_STAKE_SCRIPT:
case AddressType.BASE_PAYMENT_SCRIPT_STAKE_SCRIPT:
if (!hasPaymentCredential || !hasStakingCredential) {
throw Error(Errors.InvalidAddressParametersProvidedError)
}
break
case AddressType.ENTERPRISE_KEY:
case AddressType.ENTERPRISE_SCRIPT:
if (!hasPaymentCredential || hasStakingCredential) {
throw Error(Errors.InvalidAddressParametersProvidedError)
}
break
case AddressType.REWARD_KEY:
case AddressType.REWARD_SCRIPT:
if (!hasStakingCredential || hasPaymentCredential) {
throw Error(Errors.InvalidAddressParametersProvidedError)
}
break
default:
throw Error(Errors.InvalidAddressParametersProvidedError)
}
}

const showAddress = async (args: ParsedShowAddressArguments) => {
validateShowAddressArgs(args)
// eslint-disable-next-line no-console
console.log(`address: ${args.address}`)
return await cryptoProvider.showAddress(args)
Expand Down
78 changes: 69 additions & 9 deletions src/crypto-providers/ledgerCryptoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,68 @@ export const LedgerCryptoProvider: (
}
}

const buildSpendingParams = (
paymentPath?: BIP32Path,
paymentScriptHash?: string,
) => {
if (paymentPath != null) return {spendingPath: paymentPath}
if (paymentScriptHash != null)
return {spendingScriptHashHex: paymentScriptHash}
throw Error(Errors.InvalidAddressParametersProvidedError)
}

const buildStakingParams = (
stakingPath?: BIP32Path,
stakingScriptHash?: string,
) => {
if (stakingPath != null) return {stakingPath}
if (stakingScriptHash != null)
return {stakingScriptHashHex: stakingScriptHash}
throw Error(Errors.InvalidAddressParametersProvidedError)
}

const buildDeviceOwnedAddress = ({
paymentPath,
paymentScriptHash,
stakingPath,
stakingScriptHash,
addressType,
}: {
paymentPath?: BIP32Path
paymentScriptHash?: string
stakingPath?: BIP32Path
stakingScriptHash?: string
addressType: number
}): DeviceOwnedAddress => {
switch (addressType) {
case AddressType.BASE_PAYMENT_KEY_STAKE_KEY:
case AddressType.BASE_PAYMENT_SCRIPT_STAKE_KEY:
case AddressType.BASE_PAYMENT_KEY_STAKE_SCRIPT:
case AddressType.BASE_PAYMENT_SCRIPT_STAKE_SCRIPT:
return {
type: addressType,
params: {
...buildSpendingParams(paymentPath, paymentScriptHash),
...buildStakingParams(stakingPath, stakingScriptHash),
},
}
case AddressType.ENTERPRISE_KEY:
case AddressType.ENTERPRISE_SCRIPT:
return {
type: addressType,
params: buildSpendingParams(paymentPath, paymentScriptHash),
}
case AddressType.REWARD_KEY:
case AddressType.REWARD_SCRIPT:
return {
type: addressType,
params: buildStakingParams(stakingPath, stakingScriptHash),
}
default:
throw Error(Errors.InvalidAddressParametersProvidedError)
}
}

const showAddress = async ({
paymentPath,
paymentScriptHash,
Expand All @@ -106,15 +168,13 @@ export const LedgerCryptoProvider: (
protocolMagic,
networkId,
},
address: {
type: addressType,
params: {
spendingPath: paymentPath,
spendingScriptHashHex: paymentScriptHash,
stakingPath,
stakingScriptHashHex: stakingScriptHash,
},
},
address: buildDeviceOwnedAddress({
paymentPath,
paymentScriptHash,
stakingPath,
stakingScriptHash,
addressType,
}),
})
} catch (err) {
throw Error(failedMsg(err))
Expand Down
2 changes: 1 addition & 1 deletion src/crypto-providers/trezorCryptoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ export const TrezorCryptoProvider: () => Promise<CryptoProvider> = async () => {
if (args.hashPayload) {
throw Error(Errors.TrezorMessageHashPayloadUnsupported)
}

let request: TrezorTypes.CardanoSignMessage = {
path: args.hwSigningFileData.path,
payload: args.messageHex,
Expand Down
2 changes: 1 addition & 1 deletion test/integration/trezor/node/signMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const msgTests: {[testName: string]: TestData} = {
addressFieldHex:
'122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277',
},
}
},
}

async function testMessageSigning(
Expand Down
44 changes: 44 additions & 0 deletions test/unit/commandParser/commandParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,50 @@ describe('Command parser', () => {
assert.deepStrictEqual(parsedArgs, expectedResult)
})

it('Should parse address show command for enterprise address (payment-path only)', () => {
const args = pad([
'address',
'show',
'--payment-path',
'1852H/1815H/0H/0/0',
'--address-file',
prefix('enterprise.addr'),
])
const {parsedArgs} = parse(args)
const expectedResult = {
command: CommandType.SHOW_ADDRESS,
paymentPath: [2147485500, 2147485463, 2147483648, 0, 0],
paymentScriptHash: undefined,
stakingPath: undefined,
stakingScriptHash: undefined,
address: 'addr1vxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92su77c6m',
derivationType: undefined,
}
assert.deepStrictEqual(parsedArgs, expectedResult)
})

it('Should parse address show command for reward address (staking-path only)', () => {
const args = pad([
'address',
'show',
'--staking-path',
'1852H/1815H/0H/2/0',
'--address-file',
prefix('reward.addr'),
])
const {parsedArgs} = parse(args)
const expectedResult = {
command: CommandType.SHOW_ADDRESS,
paymentPath: undefined,
paymentScriptHash: undefined,
stakingPath: [2147485500, 2147485463, 2147483648, 2, 0],
stakingScriptHash: undefined,
address: 'stake1uyfz49rtntfa9h0s98f6s28sg69weemgjhc4e8hm66d5yacalmqha',
derivationType: undefined,
}
assert.deepStrictEqual(parsedArgs, expectedResult)
})

it('Should parse address show command with a derivation type set', () => {
const args = pad([
'shelley',
Expand Down
1 change: 1 addition & 0 deletions test/unit/commandParser/res/enterprise.addr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addr1vxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92su77c6m
1 change: 1 addition & 0 deletions test/unit/commandParser/res/reward.addr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stake1uyfz49rtntfa9h0s98f6s28sg69weemgjhc4e8hm66d5yacalmqha