@trezor/connect 9.6.2-beta.3 → 9.6.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +15 -6
  2. package/README.md +1 -1
  3. package/lib/api/applySettings.js +16 -1
  4. package/lib/api/bitcoin/signtx.d.ts +2 -2
  5. package/lib/api/bitcoin/signtx.js +1 -1
  6. package/lib/api/bleUnpair.js +0 -1
  7. package/lib/api/cardano/api/cardanoGetAddress.d.ts +1 -0
  8. package/lib/api/cardano/api/cardanoSignMessage.d.ts +25 -0
  9. package/lib/api/cardano/api/cardanoSignMessage.js +97 -0
  10. package/lib/api/cardano/api/index.d.ts +1 -0
  11. package/lib/api/cardano/api/index.js +3 -1
  12. package/lib/api/cardano/cardanoUtils.d.ts +1 -1
  13. package/lib/api/cardano/cardanoUtils.js +2 -2
  14. package/lib/api/discoverAccounts.js +61 -33
  15. package/lib/api/ethereum/api/ethereumGetAddress.js +1 -0
  16. package/lib/api/ethereum/api/ethereumSignTransaction.js +22 -4
  17. package/lib/api/ethereum/ethereumSignTx.d.ts +3 -2
  18. package/lib/api/ethereum/ethereumSignTx.js +4 -2
  19. package/lib/api/evoluGetNode.d.ts +11 -0
  20. package/lib/api/evoluGetNode.js +26 -0
  21. package/lib/api/firmware/calculateFirmwareHash.d.ts +2 -1
  22. package/lib/api/firmware/calculateFirmwareHash.js +17 -4
  23. package/lib/api/firmware/getBinary.d.ts +7 -6
  24. package/lib/api/firmware/getBinary.js +18 -6
  25. package/lib/api/firmware/index.d.ts +0 -2
  26. package/lib/api/firmware/index.js +1 -5
  27. package/lib/api/firmware/uploadFirmware.d.ts +10 -1
  28. package/lib/api/firmware/uploadFirmware.js +8 -7
  29. package/lib/api/getFeatures.d.ts +2 -1
  30. package/lib/api/index.d.ts +3 -0
  31. package/lib/api/index.js +8 -2
  32. package/lib/api/nem/nemSignTx.d.ts +1 -1
  33. package/lib/api/pushTransaction.d.ts +2 -1
  34. package/lib/api/pushTransaction.js +2 -1
  35. package/lib/api/resetDevice.js +2 -1
  36. package/lib/api/ripple/api/rippleGetAddress.d.ts +1 -0
  37. package/lib/api/signMessage.js +1 -1
  38. package/lib/api/signTransaction.d.ts +1 -1
  39. package/lib/api/solana/api/solanaGetAddress.d.ts +1 -0
  40. package/lib/api/solana/api/solanaGetPublicKey.d.ts +2 -0
  41. package/lib/api/solana/api/solanaGetPublicKey.js +3 -0
  42. package/lib/api/stellar/api/stellarGetAddress.d.ts +1 -0
  43. package/lib/api/tezos/api/tezosGetAddress.d.ts +1 -0
  44. package/lib/api/tezos/tezosSignTx.d.ts +1 -1
  45. package/lib/api/thpGetCredentials.d.ts +10 -0
  46. package/lib/api/thpGetCredentials.js +31 -0
  47. package/lib/api/thpRemoveCredentials.d.ts +8 -0
  48. package/lib/api/thpRemoveCredentials.js +32 -0
  49. package/lib/api/wipeDevice.js +6 -2
  50. package/lib/backend/Blockchain.d.ts +4 -4
  51. package/lib/backend/Blockchain.js +12 -11
  52. package/lib/constants/cardano.d.ts +3 -0
  53. package/lib/constants/cardano.js +5 -1
  54. package/lib/constants/errors.d.ts +12 -9
  55. package/lib/constants/errors.js +18 -24
  56. package/lib/core/AbstractMethod.d.ts +5 -24
  57. package/lib/core/AbstractMethod.js +2 -2
  58. package/lib/core/index.js +5 -1
  59. package/lib/core/onCallFirmwareUpdate.d.ts +13 -1
  60. package/lib/core/onCallFirmwareUpdate.js +158 -94
  61. package/lib/data/DataManager.d.ts +13 -1
  62. package/lib/data/DataManager.js +44 -14
  63. package/lib/data/coinInfo.d.ts +8 -84
  64. package/lib/data/coinInfo.js +6 -9
  65. package/lib/data/config.d.ts +26 -192
  66. package/lib/data/config.js +37 -1
  67. package/lib/data/connectSettings.js +7 -0
  68. package/lib/data/defaultFeeLevels.js +2 -1
  69. package/lib/data/deviceAuthenticityConfig.js +10 -1
  70. package/lib/data/firmwareInfo.d.ts +44 -11
  71. package/lib/data/firmwareInfo.js +360 -122
  72. package/lib/data/thpSettings.js +5 -2
  73. package/lib/data/version.d.ts +2 -1
  74. package/lib/data/version.js +4 -3
  75. package/lib/device/Device.d.ts +17 -7
  76. package/lib/device/Device.js +72 -115
  77. package/lib/device/DeviceCommands.d.ts +1 -0
  78. package/lib/device/DeviceCurrentSession.d.ts +41 -20
  79. package/lib/device/DeviceCurrentSession.js +18 -22
  80. package/lib/device/TransportList.js +10 -5
  81. package/lib/device/checkFirmwareRevision.d.ts +3 -2
  82. package/lib/device/checkFirmwareRevision.js +6 -11
  83. package/lib/device/thp/handshake.d.ts +4 -0
  84. package/lib/device/thp/handshake.js +88 -0
  85. package/lib/device/thp/index.d.ts +4 -1
  86. package/lib/device/thp/index.js +24 -6
  87. package/lib/device/thp/pairing.d.ts +16 -0
  88. package/lib/device/thp/pairing.js +230 -0
  89. package/lib/device/thp/session.d.ts +3 -0
  90. package/lib/device/thp/session.js +27 -0
  91. package/lib/device/thp/thpCall.d.ts +41 -0
  92. package/lib/device/thp/thpCall.js +57 -0
  93. package/lib/device/workflow/checkFirmwareHash.d.ts +10 -0
  94. package/lib/device/workflow/checkFirmwareHash.js +79 -0
  95. package/lib/device/workflow/checkFirmwareHashWithRetries.d.ts +9 -0
  96. package/lib/device/workflow/checkFirmwareHashWithRetries.js +41 -0
  97. package/lib/device/workflow/handshake.js +7 -6
  98. package/lib/device/workflow/validateState.js +53 -10
  99. package/lib/events/ui-request.d.ts +1 -1
  100. package/lib/factory.js +4 -0
  101. package/lib/tsconfig.lib.tsbuildinfo +1 -1
  102. package/lib/types/api/bitcoin/index.d.ts +1 -1
  103. package/lib/types/api/cardano/index.d.ts +62 -1
  104. package/lib/types/api/cardano/index.js +30 -1
  105. package/lib/types/api/cardanoSignMessage.d.ts +4 -0
  106. package/lib/types/api/cardanoSignMessage.js +3 -0
  107. package/lib/types/api/discoverAccounts.d.ts +17 -12
  108. package/lib/types/api/ethereum/index.d.ts +108 -0
  109. package/lib/types/api/ethereum/index.js +3 -0
  110. package/lib/types/api/evoluGetNode.d.ts +4 -0
  111. package/lib/types/api/evoluGetNode.js +3 -0
  112. package/lib/types/api/index.d.ts +8 -0
  113. package/lib/types/api/pushTransaction.d.ts +4 -1
  114. package/lib/types/api/pushTransaction.js +7 -1
  115. package/lib/types/api/solana/index.d.ts +3 -0
  116. package/lib/types/api/solana/index.js +2 -0
  117. package/lib/types/api/thpGetCredentials.d.ts +4 -0
  118. package/lib/types/api/thpGetCredentials.js +3 -0
  119. package/lib/types/api/thpRemoveCredentials.d.ts +7 -0
  120. package/lib/types/api/thpRemoveCredentials.js +3 -0
  121. package/lib/types/device.d.ts +9 -9
  122. package/lib/types/firmware.d.ts +15 -12
  123. package/lib/types/firmware.js +0 -3
  124. package/lib/types/params.d.ts +1 -0
  125. package/lib/types/settings.d.ts +7 -0
  126. package/lib/utils/assetUtils.d.ts +5 -2
  127. package/lib/utils/assetUtils.js +14 -18
  128. package/lib/utils/assets.native.js +0 -2
  129. package/lib/utils/firmwareReleaseConfigUtils.d.ts +8 -5
  130. package/lib/utils/firmwareReleaseConfigUtils.js +74 -59
  131. package/lib/utils/firmwareUtils.d.ts +8 -4
  132. package/lib/utils/firmwareUtils.js +62 -20
  133. package/lib/utils/formatUtils.d.ts +1 -0
  134. package/lib/utils/formatUtils.js +3 -2
  135. package/package.json +19 -18
  136. package/lib/api/firmware/getBinaryForFirmwareUpgrade.d.ts +0 -12
  137. package/lib/api/firmware/getBinaryForFirmwareUpgrade.js +0 -32
  138. package/lib/data/getLanguage.d.ts +0 -6
  139. package/lib/data/getLanguage.js +0 -10
@@ -1,3 +1,5 @@
1
+ import { Messages } from '@trezor/protobuf';
2
+ import { thp } from '@trezor/protocol';
1
3
  export declare const ERROR_CODES: {
2
4
  readonly Init_NotInitialized: "TrezorConnect not initialized";
3
5
  readonly Init_AlreadyInitialized: "TrezorConnect has been already initialized";
@@ -11,13 +13,14 @@ export declare const ERROR_CODES: {
11
13
  readonly Method_InvalidParameter: "";
12
14
  readonly Method_NotAllowed: "Method not allowed for this configuration";
13
15
  readonly Method_PermissionsNotGranted: "Permissions not granted";
14
- readonly Method_Cancel: "Cancelled";
16
+ readonly Method_Cancel: "Canceled";
15
17
  readonly Method_Interrupted: "Popup closed";
16
18
  readonly Method_UnknownCoin: "Coin not found";
17
19
  readonly Method_AddressNotMatch: "Addresses do not match";
18
20
  readonly Method_Discovery_BundleException: "";
19
21
  readonly Method_Override: "override";
20
22
  readonly Method_NoResponse: "Call resolved without response";
23
+ readonly Method_Unsupported: "Unsupported method";
21
24
  readonly Backend_NotSupported: "BlockchainLink settings not found in coins.json";
22
25
  readonly Backend_WorkerMissing: "";
23
26
  readonly Backend_Disconnected: "Backend disconnected";
@@ -37,24 +40,24 @@ export declare const ERROR_CODES: {
37
40
  readonly Device_MissingCapabilityBtcOnly: "Device is missing capability (BTC only)";
38
41
  readonly Device_ThpPairingTagInvalid: "Pairing tag mismatch";
39
42
  readonly Device_ThpStateMissing: "ThpState missing";
40
- readonly Failure_ActionCancelled: "Action cancelled by user";
41
- readonly Failure_FirmwareError: "Firmware installation failed";
43
+ readonly Device_ThpPairingMethodsException: "No common pairing methods";
42
44
  readonly Failure_UnknownCode: "Unknown error";
43
- readonly Failure_PinCancelled: "PIN cancelled";
44
- readonly Failure_PinInvalid: "PIN invalid";
45
- readonly Failure_PinMismatch: "PIN mismatch";
46
- readonly Failure_WipeCodeMismatch: "Wipe code mismatch";
47
45
  readonly Failure_EntropyCheck: "";
48
46
  readonly Deeplink_VersionMismatch: "Not compatible with current version of the app";
49
47
  };
50
- export type ErrorCode = keyof typeof ERROR_CODES;
48
+ type TypedErrorCode = keyof typeof ERROR_CODES;
49
+ export type ErrorCode = TypedErrorCode | Messages.FailureType | thp.ThpError['code'];
51
50
  export declare class TrezorError extends Error {
52
51
  code: ErrorCode;
53
52
  message: string;
54
- constructor(code: ErrorCode, message: string);
53
+ constructor(code: ErrorCode, message: string, options?: ErrorOptions);
55
54
  toString(): string;
56
55
  }
56
+ export declare const nestError: (cause: Error) => Error;
57
+ export declare class TransportError extends Error {
58
+ }
57
59
  export declare const TypedError: (id: ErrorCode, message?: string) => TrezorError;
58
60
  export declare const serializeError: (payload: any) => any;
59
61
  export declare const LIBUSB_ERROR_MESSAGE = "LIBUSB_ERROR";
62
+ export {};
60
63
  //# sourceMappingURL=errors.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LIBUSB_ERROR_MESSAGE = exports.serializeError = exports.TypedError = exports.TrezorError = exports.ERROR_CODES = void 0;
3
+ exports.LIBUSB_ERROR_MESSAGE = exports.serializeError = exports.TypedError = exports.TransportError = exports.nestError = exports.TrezorError = exports.ERROR_CODES = void 0;
4
4
  exports.ERROR_CODES = {
5
5
  Init_NotInitialized: 'TrezorConnect not initialized',
6
6
  Init_AlreadyInitialized: 'TrezorConnect has been already initialized',
@@ -14,13 +14,14 @@ exports.ERROR_CODES = {
14
14
  Method_InvalidParameter: '',
15
15
  Method_NotAllowed: 'Method not allowed for this configuration',
16
16
  Method_PermissionsNotGranted: 'Permissions not granted',
17
- Method_Cancel: 'Cancelled',
17
+ Method_Cancel: 'Canceled',
18
18
  Method_Interrupted: 'Popup closed',
19
19
  Method_UnknownCoin: 'Coin not found',
20
20
  Method_AddressNotMatch: 'Addresses do not match',
21
21
  Method_Discovery_BundleException: '',
22
22
  Method_Override: 'override',
23
23
  Method_NoResponse: 'Call resolved without response',
24
+ Method_Unsupported: 'Unsupported method',
24
25
  Backend_NotSupported: 'BlockchainLink settings not found in coins.json',
25
26
  Backend_WorkerMissing: '',
26
27
  Backend_Disconnected: 'Backend disconnected',
@@ -40,21 +41,16 @@ exports.ERROR_CODES = {
40
41
  Device_MissingCapabilityBtcOnly: 'Device is missing capability (BTC only)',
41
42
  Device_ThpPairingTagInvalid: 'Pairing tag mismatch',
42
43
  Device_ThpStateMissing: 'ThpState missing',
43
- Failure_ActionCancelled: 'Action cancelled by user',
44
- Failure_FirmwareError: 'Firmware installation failed',
44
+ Device_ThpPairingMethodsException: 'No common pairing methods',
45
45
  Failure_UnknownCode: 'Unknown error',
46
- Failure_PinCancelled: 'PIN cancelled',
47
- Failure_PinInvalid: 'PIN invalid',
48
- Failure_PinMismatch: 'PIN mismatch',
49
- Failure_WipeCodeMismatch: 'Wipe code mismatch',
50
46
  Failure_EntropyCheck: '',
51
47
  Deeplink_VersionMismatch: 'Not compatible with current version of the app',
52
48
  };
53
49
  class TrezorError extends Error {
54
50
  code;
55
51
  message;
56
- constructor(code, message) {
57
- super(message);
52
+ constructor(code, message, options) {
53
+ super(message, options);
58
54
  this.code = code;
59
55
  this.message = message;
60
56
  }
@@ -63,22 +59,20 @@ class TrezorError extends Error {
63
59
  }
64
60
  }
65
61
  exports.TrezorError = TrezorError;
66
- const TypedError = (id, message) => new TrezorError(id, message || exports.ERROR_CODES[id]);
62
+ const nestError = (cause) => cause instanceof TrezorError
63
+ ? new TrezorError(cause.code, cause.message, { cause })
64
+ : new Error(cause.message, { cause });
65
+ exports.nestError = nestError;
66
+ class TransportError extends Error {
67
+ }
68
+ exports.TransportError = TransportError;
69
+ const TypedError = (id, message) => new TrezorError(id, message || exports.ERROR_CODES[id] || id);
67
70
  exports.TypedError = TypedError;
68
71
  const serializeError = (payload) => {
69
- if (payload && payload.error instanceof Error) {
70
- return { error: payload.error.message, code: payload.error.code };
71
- }
72
- if (payload instanceof TrezorError) {
73
- return { error: payload.message, code: payload.code };
74
- }
75
- if (payload instanceof Error) {
76
- return {
77
- error: payload.message,
78
- code: 'code' in payload ? payload.code : 'Failure_UnknownCode',
79
- };
80
- }
81
- return { ...payload };
72
+ const error = payload?.error instanceof Error ? payload.error : payload;
73
+ return error instanceof Error
74
+ ? { error: error.message, code: 'code' in error ? error.code : 'Failure_UnknownCode' }
75
+ : { ...payload };
82
76
  };
83
77
  exports.serializeError = serializeError;
84
78
  exports.LIBUSB_ERROR_MESSAGE = 'LIBUSB_ERROR';
@@ -11,14 +11,16 @@ export type Payload<M> = Extract<CallMethodPayload, {
11
11
  export type MethodReturnType<M extends CallMethodPayload['method']> = CallMethodResponse<M>;
12
12
  export type MethodPermission = 'read' | 'write' | 'management' | 'push_tx';
13
13
  export type DeviceMode = typeof UI.SEEDLESS | typeof UI.BOOTLOADER | typeof UI.INITIALIZE;
14
- export interface MethodInfo {
14
+ export type MethodInfo = {
15
+ useUi: boolean;
15
16
  useDevice: boolean;
16
17
  useDeviceState: boolean;
17
18
  name: string;
18
19
  requiredPermissions: MethodPermission[];
19
20
  info: string;
21
+ precomposed?: PrecomposeResultFinal;
20
22
  confirmation?: UiRequestConfirmation['payload'];
21
- }
23
+ };
22
24
  export declare const DEFAULT_FIRMWARE_RANGE: FirmwareRange;
23
25
  export declare abstract class AbstractMethod<Name extends CallMethodPayload['method'], Params = undefined> {
24
26
  responseID: number;
@@ -64,28 +66,7 @@ export declare abstract class AbstractMethod<Name extends CallMethodPayload['met
64
66
  checkFirmwareRange(): "ui-device_firmware_old" | "ui-device_firmware_unsupported" | "ui-device_firmware_not_compatible" | "ui-device_firmware_not_installed" | undefined;
65
67
  isManagementRestricted({ popup, origin }: Pick<ConnectSettings, 'popup' | 'origin'>): boolean | undefined;
66
68
  abstract init(): void;
67
- getMethodInfo(): Promise<{
68
- useUi: boolean;
69
- useDevice: boolean;
70
- useDeviceState: boolean;
71
- name: Name;
72
- requiredPermissions: MethodPermission[];
73
- info: string;
74
- confirmation: {
75
- view: "thp-pairing-start" | "thp-pairing-failed" | "no-backup" | "export-xpub" | "export-address" | "export-account-info" | "device-management";
76
- label?: string;
77
- customConfirmButton?: {
78
- className: string;
79
- label: string;
80
- };
81
- customCancelButton?: {
82
- className: string;
83
- label: string;
84
- };
85
- analytics?: import("@trezor/connect-analytics").EventTypeDeviceSelected;
86
- } | undefined;
87
- precomposed: PrecomposeResultFinal | undefined;
88
- }>;
69
+ getMethodInfo(): MethodInfo;
89
70
  payloadToPrecomposed(): Promise<PrecomposeResultFinal | undefined>;
90
71
  checkDeviceCapability(): void;
91
72
  abstract run(): Promise<MethodReturnType<Name>>;
@@ -213,7 +213,7 @@ class AbstractMethod {
213
213
  return !allowed;
214
214
  }
215
215
  }
216
- async getMethodInfo() {
216
+ getMethodInfo() {
217
217
  return {
218
218
  useUi: this.useUi,
219
219
  useDevice: this.useDevice,
@@ -221,8 +221,8 @@ class AbstractMethod {
221
221
  name: this.name,
222
222
  requiredPermissions: this.requiredPermissions,
223
223
  info: this.info,
224
+ precomposed: undefined,
224
225
  confirmation: this.confirmation,
225
- precomposed: await this.payloadToPrecomposed(),
226
226
  };
227
227
  }
228
228
  payloadToPrecomposed() {
package/lib/core/index.js CHANGED
@@ -246,7 +246,10 @@ const onCall = async (context, message) => {
246
246
  return Promise.resolve();
247
247
  }
248
248
  if (method.payload.__info) {
249
- const response = await method.getMethodInfo();
249
+ const response = method.getMethodInfo();
250
+ if (method.payload.__precomposed) {
251
+ response.precomposed = await method.payloadToPrecomposed();
252
+ }
250
253
  sendCoreMessage((0, events_2.createResponseMessage)(method.responseID, true, response));
251
254
  return Promise.resolve();
252
255
  }
@@ -726,6 +729,7 @@ class Core extends events_1.default {
726
729
  log: _log,
727
730
  abortSignal: this.abortController.signal,
728
731
  registerEvents: registerDeviceEvents(coreContext),
732
+ uiPromises: coreContext.uiPromises,
729
733
  },
730
734
  })
731
735
  .then(payload => {
@@ -1,10 +1,21 @@
1
1
  import type { Device } from '../device/Device';
2
2
  import { DeviceList } from '../device/DeviceList';
3
- import { CoreEventMessage } from '../events';
3
+ import { CoreEventMessage, UiPromiseCreator } from '../events';
4
4
  import { CommonParams, DeviceUniquePath } from '../types';
5
5
  import { FirmwareUpdateResponse } from '../types/api/firmwareUpdate';
6
6
  import type { Log } from '../utils/debug';
7
7
  type PostMessage = (message: CoreEventMessage) => void;
8
+ type ReconnectContext = {
9
+ deviceList: DeviceList;
10
+ device: Device;
11
+ registerEvents: (device: Device) => void;
12
+ postMessage: PostMessage;
13
+ log: Log;
14
+ abortSignal: AbortSignal;
15
+ uiPromises: {
16
+ create: UiPromiseCreator;
17
+ };
18
+ };
8
19
  export type Params = {
9
20
  language?: string;
10
21
  baseUrl?: string;
@@ -18,6 +29,7 @@ type Context = {
18
29
  initDevice: (path?: DeviceUniquePath) => Promise<Device>;
19
30
  log: Log;
20
31
  abortSignal: AbortSignal;
32
+ uiPromises: ReconnectContext['uiPromises'];
21
33
  };
22
34
  type OnCallFirmwareUpdateParams = {
23
35
  params: Params;
@@ -7,7 +7,8 @@ const firmware_1 = require("../api/firmware");
7
7
  const constants_1 = require("../constants");
8
8
  const firmwareInfo_1 = require("../data/firmwareInfo");
9
9
  const events_1 = require("../events");
10
- const waitForReconnectedDevice = async ({ bootloader, method, intermediary }, { deviceList, device, registerEvents, postMessage, log, abortSignal }) => {
10
+ const types_1 = require("../types");
11
+ const waitForReconnectedDevice = async ({ bootloader, method, intermediary }, { deviceList, device, registerEvents, postMessage, log, abortSignal, uiPromises, }) => {
11
12
  const target = intermediary || !bootloader ? 'normal' : 'bootloader';
12
13
  let i = 0;
13
14
  if (method !== 'auto') {
@@ -25,6 +26,7 @@ const waitForReconnectedDevice = async ({ bootloader, method, intermediary }, {
25
26
  }
26
27
  log.debug('onCallFirmwareUpdate', `waiting for device to reconnect in ${bootloader ? 'bootloader' : 'normal'} mode`);
27
28
  let reconnectedDevice;
29
+ let thpPairingError = false;
28
30
  do {
29
31
  postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_RECONNECT, {
30
32
  device: device.toMessageObject(),
@@ -41,14 +43,29 @@ const waitForReconnectedDevice = async ({ bootloader, method, intermediary }, {
41
43
  }
42
44
  if (reconnectedDevice && !reconnectedDevice.features) {
43
45
  log.debug('onCallFirmwareUpdate', 'we were unable to read device.features on the first interaction after seeing it, retrying...');
46
+ let runFn;
47
+ if (reconnectedDevice.getThpState()?.properties) {
48
+ const uiPromise = uiPromises.create(events_1.UI.RECEIVE_CONFIRMATION, reconnectedDevice);
49
+ postMessage((0, events_1.createUiMessage)(events_1.UI.REQUEST_CONFIRMATION, {
50
+ view: thpPairingError ? 'thp-pairing-failed' : 'thp-pairing-start',
51
+ }));
52
+ const uiResp = await uiPromise.promise;
53
+ if (!uiResp.payload) {
54
+ throw constants_1.ERRORS.TypedError('Method_PermissionsNotGranted');
55
+ }
56
+ runFn = () => Promise.resolve();
57
+ }
44
58
  try {
45
59
  registerEvents(reconnectedDevice);
46
- await reconnectedDevice.run(undefined, {
60
+ await reconnectedDevice.run(runFn, {
47
61
  skipFirmwareChecks: true,
48
62
  skipLanguageChecks: true,
49
63
  });
50
64
  }
51
- catch {
65
+ catch (error) {
66
+ if (error.code === 'Device_ThpPairingTagInvalid') {
67
+ thpPairingError = true;
68
+ }
52
69
  }
53
70
  }
54
71
  i++;
@@ -76,27 +93,68 @@ const waitForReconnectedDevice = async ({ bootloader, method, intermediary }, {
76
93
  }
77
94
  return reconnectedDevice;
78
95
  };
96
+ const getRebootMethod = async ({ deviceList, device, log, postMessage, }) => {
97
+ let method = 'wait';
98
+ if (!device.bluetoothProps) {
99
+ return method;
100
+ }
101
+ const ctrl = new AbortController();
102
+ const disconnectedPromise = new Promise(resolve => {
103
+ const handleDisconnect = () => {
104
+ log.info(`waitForBluetoothReboot device-disconnected. aborted: ${ctrl.signal.aborted}`);
105
+ if (!ctrl.signal.aborted) {
106
+ ctrl.abort();
107
+ method = 'auto';
108
+ }
109
+ resolve();
110
+ };
111
+ deviceList.once('device-disconnect', handleDisconnect);
112
+ ctrl.signal.addEventListener('abort', () => {
113
+ deviceList.off('device-disconnect', handleDisconnect);
114
+ resolve();
115
+ });
116
+ });
117
+ await device.release();
118
+ const restartPromise = new Promise(resolve => {
119
+ (0, utils_1.resolveAfter)(4000).then(() => {
120
+ log.info(`waitForBluetoothReboot restartPromise. aborted: ${ctrl.signal.aborted}`);
121
+ if (!ctrl.signal.aborted) {
122
+ ctrl.abort();
123
+ postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_DISCONNECT, {
124
+ device: device.toMessageObject(),
125
+ }));
126
+ }
127
+ resolve();
128
+ });
129
+ });
130
+ await Promise.race([disconnectedPromise, restartPromise]);
131
+ return method;
132
+ };
79
133
  const getInstallationParams = (device, params) => {
80
- const btcOnly = params.btcOnly ?? device.firmwareType === 'bitcoin-only';
134
+ const btcOnly = params.btcOnly ?? device.firmwareType === types_1.FirmwareType.BitcoinOnly;
81
135
  if (!device.features.bootloader_mode) {
82
136
  const version = params.binary
83
137
  ? (0, firmware_1.parseFirmwareHeaders)(Buffer.from(params.binary)).version
84
138
  : undefined;
85
139
  const isUpdatingToNewerVersion = !version
86
- ? device.firmwareRelease?.isNewer
140
+ ? device.firmwareReleaseConfigInfo?.isNewer
87
141
  : (0, versionUtils_1.isNewer)(version, [
88
142
  device.features.major_version,
89
143
  device.features.minor_version,
90
144
  device.features.patch_version,
91
145
  ]);
92
- const isUpdatingToEqualFirmwareType = (device.firmwareType === 'bitcoin-only') === btcOnly;
146
+ const isUpdatingToEqualFirmwareType = (device.firmwareType === types_1.FirmwareType.BitcoinOnly) === btcOnly;
93
147
  const upgrade = device.atLeast('2.6.3') && isUpdatingToNewerVersion && isUpdatingToEqualFirmwareType;
94
148
  const manual = !device.atLeast(['1.10.0', '2.6.0']) && !upgrade;
95
- const language = false;
149
+ const getUpdateFlowType = () => {
150
+ if (manual)
151
+ return 'manual';
152
+ return upgrade ? 'reboot_and_upgrade' : 'reboot_and_wait';
153
+ };
96
154
  return {
97
155
  manual,
98
156
  upgrade,
99
- language,
157
+ updateFlowType: getUpdateFlowType(),
100
158
  btcOnly,
101
159
  };
102
160
  }
@@ -104,80 +162,100 @@ const getInstallationParams = (device, params) => {
104
162
  return {
105
163
  manual: false,
106
164
  upgrade: false,
107
- language: false,
165
+ updateFlowType: 'unknown_flow',
108
166
  btcOnly,
109
167
  };
110
168
  }
111
169
  };
112
170
  const getFwHeader = (binary) => Buffer.from(binary.slice(0, 6000)).toString('hex');
113
- const getBinaryHelper = (device, params, log, postMessage, btcOnly) => {
171
+ const getBinaryHelper = async ({ device, params, firmwareType, isIntermediary, log, }) => {
114
172
  if (params.binary) {
115
- return {
173
+ return Promise.resolve({
116
174
  binary: params.binary,
117
175
  binaryVersion: (0, firmware_1.parseFirmwareHeaders)(Buffer.from(params.binary)).version,
118
176
  releaseVersion: undefined,
119
- };
177
+ });
120
178
  }
121
- if (!device.firmwareRelease) {
122
- throw constants_1.ERRORS.TypedError('Runtime', 'device.firmwareRelease is not set');
179
+ if (!device.firmwareReleaseConfigInfo) {
180
+ throw constants_1.ERRORS.TypedError('Runtime', 'device.firmwareReleaseConfigInfo is not set');
123
181
  }
124
- const { intermediaryVersion, release: { version }, } = device.firmwareRelease;
125
- log.debug('onCallFirmwareUpdate loading binary', 'intermediaryVersion', intermediaryVersion, 'version', version, 'btcOnly', btcOnly);
126
- postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_PROGRESS, {
127
- device: device.toMessageObject(),
128
- operation: 'downloading',
129
- progress: 0,
130
- }));
131
- return (0, firmware_1.getBinaryForFirmwareUpgrade)({
132
- features: device.features,
133
- releases: (0, firmwareInfo_1.getReleases)(device.features?.internal_model),
134
- baseUrl: params.baseUrl || 'https://data.trezor.io',
135
- version,
136
- btcOnly,
137
- intermediaryVersion,
138
- })
139
- .then(res => {
140
- if (res.byteLength < 200) {
141
- throw constants_1.ERRORS.TypedError('Runtime', 'Firmware binary is too small');
142
- }
143
- return res;
144
- })
145
- .then(res => {
146
- postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_PROGRESS, {
147
- device: device.toMessageObject(),
148
- operation: 'downloading',
149
- progress: 100,
150
- }));
151
- return {
152
- binary: res,
153
- binaryVersion: (0, firmware_1.parseFirmwareHeaders)(Buffer.from(res)).version,
154
- releaseVersion: version,
155
- };
182
+ const deviceModel = device.features?.internal_model;
183
+ const { release: { version }, intermediary, } = device.firmwareReleaseConfigInfo;
184
+ log.debug('onCallFirmwareUpdate loading binary', 'isIntermediary', isIntermediary, 'version', version, 'firmwareType', firmwareType, 'deviceModel', deviceModel);
185
+ const release = await (0, firmwareInfo_1.getReleaseByVersion)(device.features, version, firmwareType);
186
+ if (!release) {
187
+ throw new Error('Missing Firmware release for device');
188
+ }
189
+ const { baseUrl, path } = (0, firmwareInfo_1.getFirmwareLocation)({
190
+ firmwareVersion: version,
191
+ remotePath: release.url,
192
+ deviceModel,
193
+ firmwareType,
194
+ intermediaryVersion: isIntermediary && intermediary ? intermediary.version : undefined,
156
195
  });
196
+ return (0, firmware_1.getBinary)({ baseUrl, path, version });
157
197
  };
158
198
  const onCallFirmwareUpdate = async ({ params, context, }) => {
159
199
  const { deviceList, registerEvents, postMessage, initDevice, log } = context;
160
200
  log.debug('onCallFirmwareUpdate with params: ', params);
201
+ const firmwareType = params.btcOnly ? types_1.FirmwareType.BitcoinOnly : types_1.FirmwareType.Universal;
161
202
  const device = await initDevice(params?.device?.path);
203
+ if (!device.features) {
204
+ throw constants_1.ERRORS.TypedError('Device_NotFound', 'Device missing features');
205
+ }
162
206
  if (deviceList.getDeviceCount() > 1) {
163
207
  throw constants_1.ERRORS.TypedError('Device_MultipleNotSupported', 'Firmware update allowed with only 1 device connected');
164
208
  }
165
209
  log.debug('onCallFirmwareUpdate', 'device', device);
166
210
  registerEvents(device);
167
- const { manual, upgrade, language, btcOnly } = getInstallationParams(device, params);
211
+ const { manual, upgrade, updateFlowType, btcOnly } = getInstallationParams(device, params);
168
212
  log.debug('onCallFirmwareUpdate', 'installation params', {
169
213
  manual,
170
214
  upgrade,
171
- language,
215
+ updateFlowType,
172
216
  btcOnly,
173
217
  });
174
- let binaryInfo = await getBinaryHelper(device, params, log, postMessage, btcOnly);
175
- const { binary } = binaryInfo;
218
+ const intermediary = !params.binary && device?.firmwareReleaseConfigInfo?.intermediary;
219
+ postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_PROGRESS, {
220
+ device: device.toMessageObject(),
221
+ operation: 'downloading',
222
+ progress: 0,
223
+ }));
224
+ let intermediaryBinaryInfo;
225
+ let finalBinaryInfo;
226
+ const fwFetchPromises = [];
227
+ if (intermediary) {
228
+ fwFetchPromises.push(getBinaryHelper({ device, params, firmwareType, isIntermediary: true, log }));
229
+ }
230
+ fwFetchPromises.push(getBinaryHelper({ device, params, firmwareType, isIntermediary: false, log }));
231
+ const [firstResult, finalResult] = await Promise.all(fwFetchPromises);
232
+ if (intermediary) {
233
+ intermediaryBinaryInfo = firstResult;
234
+ finalBinaryInfo = finalResult;
235
+ }
236
+ else {
237
+ finalBinaryInfo = firstResult;
238
+ }
239
+ const firstBinaryInfo = intermediary ? intermediaryBinaryInfo : finalBinaryInfo;
240
+ if (!firstBinaryInfo) {
241
+ throw new Error('Missing binary, something went wrong.');
242
+ }
243
+ postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_PROGRESS, {
244
+ device: device.toMessageObject(),
245
+ operation: 'downloading',
246
+ progress: 100,
247
+ }));
248
+ postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_DOWNLOADED, {
249
+ binary: finalBinaryInfo.binary,
250
+ binaryVersion: finalBinaryInfo.binaryVersion,
251
+ releaseVersion: finalBinaryInfo.releaseVersion,
252
+ firmwareType: device.firmwareType,
253
+ internalModel: device.features.internal_model,
254
+ }));
176
255
  const deviceInitiallyConnectedInBootloader = device.features.bootloader_mode;
177
- const deviceInitiallyConnectedWithoutFirmware = device.features.firmware_present === false;
178
256
  let reconnectedDevice = device;
179
257
  if (deviceInitiallyConnectedInBootloader) {
180
- log.warn('onCallFirmwareUpdate', 'device is already in bootloader mode. language will not be updated');
258
+ log.warn('onCallFirmwareUpdate', 'device is already in bootloader mode.');
181
259
  await device.acquire();
182
260
  }
183
261
  else if (manual) {
@@ -187,73 +265,59 @@ const onCallFirmwareUpdate = async ({ params, context, }) => {
187
265
  const rebootParams = upgrade
188
266
  ? {
189
267
  boot_command: constants_1.PROTO.BootCommand.INSTALL_UPGRADE,
190
- firmware_header: getFwHeader(binary),
268
+ firmware_header: getFwHeader(firstBinaryInfo.binary),
191
269
  }
192
270
  : {};
193
271
  await device.acquire();
194
- const targetLanguage = params.language || device.features.language || 'en-US';
195
- const languageBlob = device.firmwareRelease && language && targetLanguage !== 'en-US'
196
- ? await (0, firmware_1.getLanguage)({
197
- language: targetLanguage,
198
- version: device.firmwareRelease.release.version,
199
- internal_model: device.features.internal_model,
200
- }).catch(() => {
201
- })
202
- : null;
203
272
  const disconnectedPromise = new Promise(resolve => {
204
273
  deviceList.once('device-disconnect', resolve);
205
274
  });
206
- if (!languageBlob) {
207
- await device.getCommands().typedCall('RebootToBootloader', 'Success', rebootParams);
208
- }
209
- else {
210
- let rebootResponse = await device.getCommands().typedCall('RebootToBootloader', ['DataChunkRequest', 'Success'], { ...rebootParams, language_data_length: languageBlob?.byteLength });
211
- log.debug('onCallFirmwareUpdate', 'RebootToBootloader response', rebootResponse.message);
212
- while (languageBlob && rebootResponse.type !== 'Success') {
213
- const start = rebootResponse.message.data_offset;
214
- const end = rebootResponse.message.data_offset + rebootResponse.message.data_length;
215
- const chunk = languageBlob.slice(start, end);
216
- rebootResponse = await device.getCommands().typedCall('DataChunkAck', ['DataChunkRequest', 'Success'], { data_chunk: Buffer.from(chunk).toString('hex') });
217
- }
218
- }
275
+ await device.getCommands().typedCall('RebootToBootloader', 'Success', rebootParams);
219
276
  log.info('onCallFirmwareUpdate', 'waiting for disconnected event after rebootToBootloader...');
277
+ if (device.bluetoothProps) {
278
+ await device.release();
279
+ postMessage((0, events_1.createUiMessage)(events_1.UI.FIRMWARE_DISCONNECT, {
280
+ device: device.toMessageObject(),
281
+ }));
282
+ }
220
283
  await disconnectedPromise;
221
284
  if (device.features.major_version === 1) {
222
285
  await (0, utils_1.resolveAfter)(2000);
223
286
  }
224
287
  reconnectedDevice = await waitForReconnectedDevice({ bootloader: true, method: 'auto' }, { ...context, device });
225
288
  }
226
- const intermediary = !params.binary && device.firmwareRelease?.intermediaryVersion;
227
289
  const bootloaderVersion = reconnectedDevice.getVersion();
228
290
  await reconnectedDevice.initialize(false);
229
- let stripped = (0, firmware_1.stripFwHeaders)(binary);
230
- await (0, firmware_1.uploadFirmware)(reconnectedDevice.getCommands().typedCall, postMessage, reconnectedDevice, { payload: !intermediary && (0, firmware_1.shouldStripFwHeaders)(device.features) ? stripped : binary });
291
+ let stripped = (0, firmware_1.stripFwHeaders)(firstBinaryInfo.binary);
292
+ const payload = !intermediary && (0, firmware_1.shouldStripFwHeaders)(device.features) ? stripped : firstBinaryInfo.binary;
293
+ await (0, firmware_1.uploadFirmware)({
294
+ typedCall: reconnectedDevice.getCommands().typedCall,
295
+ postMessage,
296
+ device: reconnectedDevice,
297
+ firmwareUploadRequest: { payload },
298
+ updateFlowType,
299
+ });
231
300
  log.info('onCallFirmwareUpdate', 'firmware uploaded');
232
301
  if (intermediary) {
233
302
  log.info('onCallFirmwareUpdate', '...but it was the intermediary firmware, so one more go');
234
303
  reconnectedDevice = await waitForReconnectedDevice({ bootloader: true, method: 'manual', intermediary: true }, { ...context, device: reconnectedDevice });
235
- binaryInfo = await getBinaryHelper(reconnectedDevice, params, log, postMessage, btcOnly);
236
- stripped = (0, firmware_1.stripFwHeaders)(binaryInfo.binary);
304
+ stripped = (0, firmware_1.stripFwHeaders)(finalBinaryInfo.binary);
237
305
  await reconnectedDevice.initialize(false);
238
- await (0, firmware_1.uploadFirmware)(reconnectedDevice.getCommands().typedCall, postMessage, reconnectedDevice, { payload: stripped });
239
- }
240
- reconnectedDevice = await waitForReconnectedDevice({ bootloader: false, method: 'wait' }, { ...context, device: reconnectedDevice });
241
- if (reconnectedDevice.atLeast('2.7.0') &&
242
- deviceInitiallyConnectedWithoutFirmware &&
243
- params.language) {
244
- try {
245
- log.info('onCallFirmwareUpdate', 'changing language for fresh device to: ', params.language);
246
- await reconnectedDevice.changeLanguage({ language: params.language });
247
- }
248
- catch (err) {
249
- log.error('onCallFirmwareUpdate', 'changeLanguage failed silently: ', err);
250
- }
306
+ await (0, firmware_1.uploadFirmware)({
307
+ typedCall: reconnectedDevice.getCommands().typedCall,
308
+ postMessage,
309
+ device: reconnectedDevice,
310
+ firmwareUploadRequest: { payload: stripped },
311
+ updateFlowType,
312
+ });
251
313
  }
314
+ const method = await getRebootMethod({ deviceList, device, log, postMessage });
315
+ reconnectedDevice = await waitForReconnectedDevice({ bootloader: false, method }, { ...context, device: reconnectedDevice });
252
316
  const installedVersion = reconnectedDevice.getVersion();
253
317
  if (!bootloaderVersion || !installedVersion) {
254
318
  throw constants_1.ERRORS.TypedError('Runtime', 'reconnectedDevice.installedVersion is not set');
255
319
  }
256
- const { binaryVersion, releaseVersion } = binaryInfo;
320
+ const { binaryVersion, releaseVersion } = finalBinaryInfo;
257
321
  const assertBinaryVersion = (0, versionUtils_1.isEqual)(installedVersion, binaryVersion);
258
322
  const assertReleaseVersion = releaseVersion ? (0, versionUtils_1.isEqual)(installedVersion, releaseVersion) : true;
259
323
  await reconnectedDevice.release();
@@ -1,3 +1,4 @@
1
+ import { ConditionalRelease, DeviceModelInternal, FirmwareReleaseConfig, FirmwareType, IntermediaryReleaseConfig, ReleasesConfig } from '@trezor/device-utils';
1
2
  import type { ConnectSettings, LocalFirmwares } from '../types/settings';
2
3
  type AssetKeys = `firmware-${string}` | 'coins' | 'coinsEth';
3
4
  type AssetCollection = {
@@ -8,12 +9,23 @@ export declare class DataManager {
8
9
  private static settings;
9
10
  private static messages;
10
11
  private static localFirmwares;
11
- static load(settings: ConnectSettings, withAssets?: boolean): void;
12
+ private static firmwareReleasesConfig;
13
+ private static firmwareIntermediaryReleasesConfig;
14
+ private static localFirmwareReleaseConfig;
15
+ static load(settings: ConnectSettings, withAssets?: boolean, onlyLocalFirmwareConfig?: boolean): Promise<void>;
16
+ static prepareLocalFirmwareReleaseData(): void;
17
+ static loadFirmwareRelaseConfig(onlyLocal: boolean): Promise<void>;
12
18
  static getProtobufMessages(): Record<string, any>;
13
19
  static getSettings(key?: undefined): ConnectSettings;
14
20
  static getSettings<T extends keyof ConnectSettings>(key: T): ConnectSettings[T];
15
21
  static setLocalFirmwares(firmwares: LocalFirmwares): void;
16
22
  static getLocalFirmwares(): LocalFirmwares;
23
+ static setLocalFirmwareReleaseConfig(localFirmwareReleaseConfig: FirmwareReleaseConfig): void;
24
+ static getLocalFirmwareReleaseConfig(): FirmwareReleaseConfig;
25
+ static setFirmwareReleaseConfig(releaseConfig: ReleasesConfig): void;
26
+ static getFirmwareReleaseConfig(): Partial<Record<"T1B1" | "T2T1" | "T2B1" | "T3B1" | "T3T1" | "T3W1" | "UNKNOWN", Record<FirmwareType, ConditionalRelease>>>;
27
+ static setFirmwareIntermediaryReleaseConfig(intermediariesConfig: Record<DeviceModelInternal, IntermediaryReleaseConfig[]>): void;
28
+ static getFirmwareIntermediaryReleaseConfig(): Record<"T1B1" | "T2T1" | "T2B1" | "T3B1" | "T3T1" | "T3W1" | "UNKNOWN", IntermediaryReleaseConfig[]> | undefined;
17
29
  }
18
30
  export {};
19
31
  //# sourceMappingURL=DataManager.d.ts.map