@onekeyfe/hd-core 1.2.0-alpha.106 → 1.2.0-alpha.107

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 (61) hide show
  1. package/__tests__/DeviceCommands.test.ts +28 -100
  2. package/__tests__/device-lifecycle-events.test.ts +2 -113
  3. package/__tests__/device-pool-state.test.ts +0 -25
  4. package/__tests__/deviceSettings.test.ts +1 -0
  5. package/__tests__/deviceUploadNft.test.ts +4 -33
  6. package/__tests__/get-device-state.test.ts +13 -52
  7. package/__tests__/logBlockEvent.test.ts +1 -13
  8. package/__tests__/protocol-v2.test.ts +121 -758
  9. package/__tests__/refresh-device-state.test.ts +1 -3
  10. package/__tests__/search-devices.test.ts +38 -32
  11. package/dist/api/FirmwareUpdateV4.d.ts +3 -10
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/UploadPortfolio.d.ts +1 -1
  14. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  15. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
  16. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  17. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
  18. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  19. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/device/Device.d.ts +2 -7
  22. package/dist/device/Device.d.ts.map +1 -1
  23. package/dist/device/DeviceCommands.d.ts.map +1 -1
  24. package/dist/device/DevicePool.d.ts.map +1 -1
  25. package/dist/events/logBlockEvent.d.ts.map +1 -1
  26. package/dist/index.d.ts +16 -12
  27. package/dist/index.js +201 -446
  28. package/dist/protocols/protocol-v2/features.d.ts +1 -9
  29. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  30. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  31. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  32. package/dist/types/api/protocolV2.d.ts +1 -1
  33. package/dist/types/api/protocolV2.d.ts.map +1 -1
  34. package/dist/types/device.d.ts.map +1 -1
  35. package/dist/utils/pro2Nft.d.ts +0 -7
  36. package/dist/utils/pro2Nft.d.ts.map +1 -1
  37. package/package.json +4 -6
  38. package/src/api/FirmwareUpdateV4.ts +174 -312
  39. package/src/api/SearchDevices.ts +2 -2
  40. package/src/api/UploadPortfolio.ts +2 -9
  41. package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
  42. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
  43. package/src/api/ton/TonSignMessage.ts +3 -5
  44. package/src/core/index.ts +2 -6
  45. package/src/device/Device.ts +31 -53
  46. package/src/device/DeviceCommands.ts +14 -4
  47. package/src/device/DevicePool.ts +2 -6
  48. package/src/events/logBlockEvent.ts +1 -14
  49. package/src/protocols/protocol-v2/features.ts +2 -19
  50. package/src/protocols/protocol-v2/index.ts +0 -2
  51. package/src/types/api/protocolV2.ts +1 -1
  52. package/src/types/device.ts +2 -1
  53. package/src/utils/deviceSettings.ts +1 -1
  54. package/src/utils/pro2Nft.ts +8 -31
  55. package/__tests__/base64Data.test.ts +0 -70
  56. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +0 -145
  57. package/__tests__/resourceBase64Boundary.test.ts +0 -48
  58. package/__tests__/ton-sign-message.test.ts +0 -84
  59. package/dist/api/helpers/base64Data.d.ts +0 -16
  60. package/dist/api/helpers/base64Data.d.ts.map +0 -1
  61. package/src/api/helpers/base64Data.ts +0 -85
@@ -42,8 +42,8 @@ export default class SearchDevices extends BaseMethod {
42
42
  ...device,
43
43
  connectId: device.id,
44
44
  serialNo: null,
45
- // Legacy BLE discovery identifier. The physical serial number is unavailable here.
46
- uuid: device.id,
45
+ // BLE discovery cannot provide a physical-device identity before initialization.
46
+ uuid: '',
47
47
  deviceId: null,
48
48
  name: bleName || device.name,
49
49
  deviceType: getDeviceTypeByBleName(bleName),
@@ -1,28 +1,21 @@
1
1
  import { createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import { supportsProtocolV2Message } from '../protocols/protocol-v2/features';
4
- import { decodeCanonicalBase64 } from './helpers/base64Data';
5
4
  import FileWrite from './FileWrite';
6
5
 
7
6
  export type UploadPortfolioParams = {
8
- packageBase64: string;
7
+ packageBytes: ArrayBuffer | Uint8Array | Blob;
9
8
  timeoutMs?: number | string;
10
9
  };
11
10
 
12
11
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
13
12
  const PORTFOLIO_CHUNK_SIZE = 2048;
14
- const PORTFOLIO_PACKAGE_MAX_BYTES = 128 * 1024;
15
13
  const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
16
14
  const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
17
15
 
18
16
  export default class UploadPortfolio extends FileWrite {
19
17
  init() {
20
- const { packageBase64, timeoutMs } = this.payload as UploadPortfolioParams;
21
- const packageBytes = decodeCanonicalBase64({
22
- value: packageBase64,
23
- parameterName: 'packageBase64',
24
- maxBytes: PORTFOLIO_PACKAGE_MAX_BYTES,
25
- });
18
+ const { packageBytes, timeoutMs } = this.payload as UploadPortfolioParams;
26
19
  this.payload = {
27
20
  ...this.payload,
28
21
  path: PORTFOLIO_PENDING_PATH,
@@ -7,26 +7,21 @@ import {
7
7
  PRO2_NFT_DEFAULT_PACE_MS,
8
8
  PRO2_NFT_DEFAULT_TIMEOUT_MS,
9
9
  PRO2_NFT_DIRECTORY,
10
- PRO2_NFT_IMAGE_HEIGHT,
11
- PRO2_NFT_IMAGE_WIDTH,
12
10
  PRO2_NFT_MAX_CHUNK_SIZE,
13
11
  PRO2_NFT_MAX_ITEMS,
14
12
  PRO2_NFT_MIN_CHUNK_SIZE,
15
- PRO2_NFT_THUMBNAIL_HEIGHT,
16
- PRO2_NFT_THUMBNAIL_WIDTH,
17
13
  type Pro2NftBundle,
18
- buildPro2NftBundleFromEncodedImages,
14
+ type Pro2NftImage,
15
+ buildPro2NftBundle,
19
16
  getCompletePro2NftBasenames,
20
17
  } from '../../utils/pro2Nft';
21
- import { encodePro2Image } from '../../utils/pro2Wallpaper';
22
18
  import { BaseMethod } from '../BaseMethod';
23
- import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
24
19
  import { invalidParameter } from '../helpers/filesystemValidation';
25
20
  import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
26
21
 
27
22
  export type DeviceUploadNftParams = {
28
- imageJpegBase64: string;
29
- thumbnailJpegBase64: string;
23
+ image: Pro2NftImage;
24
+ thumbnail: Pro2NftImage;
30
25
  title: string;
31
26
  subtitle: string;
32
27
  timestampMs?: number;
@@ -59,8 +54,8 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
59
54
 
60
55
  init() {
61
56
  const {
62
- imageJpegBase64,
63
- thumbnailJpegBase64,
57
+ image,
58
+ thumbnail,
64
59
  title,
65
60
  subtitle,
66
61
  timestampMs = Date.now(),
@@ -84,51 +79,8 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
84
79
  throw invalidParameter('Parameter [timeoutMs] must be a positive integer.');
85
80
  }
86
81
 
87
- const encodedImage = (() => {
88
- const decoded = decodeJpegBase64ToRgba({
89
- jpegBase64: imageJpegBase64,
90
- parameterName: 'imageJpegBase64',
91
- expectedWidth: PRO2_NFT_IMAGE_WIDTH,
92
- expectedHeight: PRO2_NFT_IMAGE_HEIGHT,
93
- });
94
- return encodePro2Image({
95
- width: PRO2_NFT_IMAGE_WIDTH,
96
- height: PRO2_NFT_IMAGE_HEIGHT,
97
- rgba: decoded.data,
98
- alphaMode: 'black-background',
99
- }).data;
100
- })();
101
- const encodedThumbnail = (() => {
102
- const decoded = decodeJpegBase64ToRgba({
103
- jpegBase64: thumbnailJpegBase64,
104
- parameterName: 'thumbnailJpegBase64',
105
- expectedWidth: PRO2_NFT_THUMBNAIL_WIDTH,
106
- expectedHeight: PRO2_NFT_THUMBNAIL_HEIGHT,
107
- });
108
- return encodePro2Image({
109
- width: PRO2_NFT_THUMBNAIL_WIDTH,
110
- height: PRO2_NFT_THUMBNAIL_HEIGHT,
111
- rgba: decoded.data,
112
- alphaMode: 'black-background',
113
- }).data;
114
- })();
115
- this.bundle = buildPro2NftBundleFromEncodedImages({
116
- image: encodedImage,
117
- thumbnail: encodedThumbnail,
118
- title,
119
- subtitle,
120
- timestampMs,
121
- });
122
- this.params = {
123
- imageJpegBase64,
124
- thumbnailJpegBase64,
125
- title,
126
- subtitle,
127
- timestampMs,
128
- chunkSize,
129
- paceMs,
130
- timeoutMs,
131
- };
82
+ this.bundle = buildPro2NftBundle({ image, thumbnail, title, subtitle, timestampMs });
83
+ this.params = { image, thumbnail, title, subtitle, timestampMs, chunkSize, paceMs, timeoutMs };
132
84
  this.unlockPolicy = 'none';
133
85
  this.skipForceUpdateCheck = true;
134
86
  this.useDevicePassphraseState = false;
@@ -1,13 +1,10 @@
1
1
  import { blake2s } from '@noble/hashes/blake2s';
2
2
  import { bytesToHex } from '@noble/hashes/utils';
3
- import { createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
4
3
 
5
4
  import { BaseMethod } from '../BaseMethod';
6
- import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
7
5
  import { invalidParameter } from '../helpers/filesystemValidation';
8
6
  import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
9
7
  import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
10
- import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
11
8
  import {
12
9
  PRO2_WALLPAPER_HEIGHT,
13
10
  PRO2_WALLPAPER_WIDTH,
@@ -16,7 +13,9 @@ import {
16
13
  } from '../../utils/pro2Wallpaper';
17
14
 
18
15
  export type DeviceUploadWallpaperParams = {
19
- jpegBase64: string;
16
+ width: number;
17
+ height: number;
18
+ rgba: Uint8Array | ArrayBuffer;
20
19
  fileName?: string;
21
20
  chunkSize?: number;
22
21
  };
@@ -30,9 +29,6 @@ export type DeviceUploadWallpaperResponse = {
30
29
 
31
30
  const WALLPAPER_DIRECTORY = 'vol1:/wallpapers';
32
31
  const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
33
- const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
34
- const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
35
- const FILESYSTEM_DIR_MAKE_MESSAGE_TYPE = 60809;
36
32
 
37
33
  function normalizeFileName(fileName: string | undefined, data: Uint8Array): string {
38
34
  if (fileName !== undefined && (!fileName || !SAFE_FILE_NAME.test(fileName))) {
@@ -58,45 +54,31 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
58
54
  private path = '';
59
55
 
60
56
  init() {
61
- const { jpegBase64, fileName, chunkSize } = this.payload;
57
+ const { width, height, rgba, fileName, chunkSize } = this.payload;
58
+ if (width !== PRO2_WALLPAPER_WIDTH || height !== PRO2_WALLPAPER_HEIGHT) {
59
+ throw invalidParameter(
60
+ `Pro2 wallpaper dimensions must be ${PRO2_WALLPAPER_WIDTH}x${PRO2_WALLPAPER_HEIGHT}.`
61
+ );
62
+ }
63
+ if (!(rgba instanceof ArrayBuffer) && !ArrayBuffer.isView(rgba)) {
64
+ throw invalidParameter('Parameter [rgba] must be an ArrayBuffer or Uint8Array.');
65
+ }
62
66
  if (chunkSize !== undefined && (!Number.isInteger(chunkSize) || chunkSize <= 0)) {
63
67
  throw invalidParameter('Parameter [chunkSize] must be a positive integer.');
64
68
  }
65
69
 
66
- const decoded = decodeJpegBase64ToRgba({
67
- jpegBase64,
68
- parameterName: 'jpegBase64',
69
- expectedWidth: PRO2_WALLPAPER_WIDTH,
70
- expectedHeight: PRO2_WALLPAPER_HEIGHT,
71
- });
72
- this.encoded = encodePro2Wallpaper({
73
- width: PRO2_WALLPAPER_WIDTH,
74
- height: PRO2_WALLPAPER_HEIGHT,
75
- rgba: decoded.data,
76
- });
70
+ const rgbaBytes =
71
+ rgba instanceof ArrayBuffer
72
+ ? rgba
73
+ : new Uint8Array(rgba.buffer, rgba.byteOffset, rgba.byteLength);
74
+ this.encoded = encodePro2Wallpaper({ width, height, rgba: rgbaBytes });
77
75
  this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
78
- this.params = { jpegBase64, fileName, chunkSize };
76
+ this.params = { width, height, rgba: rgbaBytes, fileName, chunkSize };
79
77
  this.unlockPolicy = 'none';
80
78
  this.skipForceUpdateCheck = true;
81
79
  this.useDevicePassphraseState = false;
82
80
  }
83
81
 
84
- private async assertCapabilities() {
85
- const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
86
- const requiredMessageTypes = [
87
- DEVICE_SETTINGS_SET_MESSAGE_TYPE,
88
- FILESYSTEM_FILE_WRITE_MESSAGE_TYPE,
89
- FILESYSTEM_DIR_MAKE_MESSAGE_TYPE,
90
- ];
91
- if (
92
- requiredMessageTypes.some(
93
- messageType => !supportsProtocolV2Message(protocolInfo, messageType)
94
- )
95
- ) {
96
- throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
97
- }
98
- }
99
-
100
82
  private async ensureDirectory() {
101
83
  if (this.directoryReady) return;
102
84
  try {
@@ -137,7 +119,6 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
137
119
  async run(): Promise<DeviceUploadWallpaperResponse> {
138
120
  const { encoded } = this;
139
121
  if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
140
- await this.assertCapabilities();
141
122
  await this.ensureDirectory();
142
123
  await this.upload();
143
124
  const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
@@ -174,14 +174,12 @@ export default class TonSignMessage extends BaseMethod<HardwareTonSignMessage> {
174
174
  if (!request.init_data_length) {
175
175
  const deviceType = this.device.getCurrentDeviceType();
176
176
  const hasClassic = DeviceModelToTypes.model_classic1s.includes(deviceType);
177
- const signingMessage = request.signing_message ?? request.signning_message;
178
- // Blind signing omits the signing message in both protocol schemas.
179
- const shouldSkipValidation = signingMessage == null;
177
+ // use signing_message_repr sign, not exists signning_message, skip validate
178
+ const hasSigningMessageRepr = request.signning_message == null;
180
179
 
181
180
  return Promise.resolve({
182
181
  ...request,
183
- signing_message: signingMessage,
184
- skip_validate: hasClassic || shouldSkipValidation,
182
+ skip_validate: hasClassic || hasSigningMessageRepr,
185
183
  });
186
184
  }
187
185
 
package/src/core/index.ts CHANGED
@@ -136,9 +136,6 @@ const toError = (error: unknown): Error | undefined => {
136
136
  }
137
137
  };
138
138
 
139
- const isExpectedCompatibilityError = (error: unknown) =>
140
- error instanceof HardwareError && error.errorCode === HardwareErrorCode.DeviceNotSupportMethod;
141
-
142
139
  const updateMethodRequestContext = (method: BaseMethod, updates: any) => {
143
140
  if (method.requestContext) {
144
141
  updateRequestContext(method.requestContext.responseID, updates);
@@ -696,9 +693,7 @@ const onCallDevice = async (
696
693
  try {
697
694
  return await task.callPromise.promise;
698
695
  } catch (e) {
699
- if (!isExpectedCompatibilityError(e)) {
700
- Log.debug('Device Run Error: ', e);
701
- }
696
+ Log.debug('Device Run Error: ', e);
702
697
  completeMethodRequestContext(method, e);
703
698
  return createResponseMessage(method.responseID, false, { error: e });
704
699
  }
@@ -1253,6 +1248,7 @@ const cleanup = () => {
1253
1248
  pendingUiPromises,
1254
1249
  ERRORS.TypedError(HardwareErrorCode.ActionCancelled, 'UI request was cancelled')
1255
1250
  );
1251
+ Log.debug('Cleanup...');
1256
1252
  };
1257
1253
 
1258
1254
  const removeDeviceListener = (device: Device) => {
@@ -61,7 +61,6 @@ import {
61
61
  PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
62
62
  type ProtocolV2RuntimeMode,
63
63
  getProtocolV2RuntimeMode,
64
- isLegacyProtocolV2ProtocolInfo,
65
64
  requestProtocolV2DeviceInfo,
66
65
  requestProtocolV2DeviceStatus,
67
66
  requestProtocolV2ProtocolInfo,
@@ -486,10 +485,6 @@ export class Device extends EventEmitter {
486
485
  }
487
486
 
488
487
  this.commands = new DeviceCommands(this, this.mainId ?? '');
489
- // Protocol V2 runtime metadata belongs to one active transport link. A
490
- // successful acquire creates a fresh link/session, so never carry cached
491
- // ProtocolInfo or pre-initialize state across that boundary.
492
- this.invalidateProtocolV2RuntimeState();
493
488
  } catch (error) {
494
489
  if (options?.forceProtocolDetection) {
495
490
  this.originalDescriptor.protocolType = previousProtocol;
@@ -982,7 +977,11 @@ export class Device extends EventEmitter {
982
977
  });
983
978
  // The default request excludes SE/hash data and therefore uses basic scope.
984
979
  // Full version and verification data require getDeviceState({ scope: 'firmware' }).
985
- await this.probeProtocolV2RuntimeState(deviceInfo, options?.protocolV2DeviceInfoTimeoutMs);
980
+ const features = await this.probeProtocolV2RuntimeState(
981
+ deviceInfo,
982
+ options?.protocolV2DeviceInfoTimeoutMs
983
+ );
984
+ Log.debug('Protocol V2 features:', features);
986
985
  } catch (error) {
987
986
  Log.error('Protocol V2 initialization failed:', error);
988
987
  throw error;
@@ -1054,13 +1053,7 @@ export class Device extends EventEmitter {
1054
1053
  }
1055
1054
 
1056
1055
  if (refresh.has('status') && !initializedWithDeviceInfo) {
1057
- const cachedMode = this.state?.status.mode;
1058
- await this.probeProtocolV2RuntimeState(refreshedDeviceInfo, undefined, {
1059
- // Loader firmware does not support DeviceStatusGet. Renegotiate ProtocolInfo
1060
- // during an explicit refresh so a device rebooted into application firmware
1061
- // can leave the cached loader state.
1062
- forceRuntimeContextRefresh: cachedMode === 'bootloader' || cachedMode === 'romloader',
1063
- });
1056
+ await this.probeProtocolV2RuntimeState(refreshedDeviceInfo);
1064
1057
  }
1065
1058
 
1066
1059
  if (refresh.has('settings') && this.state?.status.mode === 'normal') {
@@ -1108,10 +1101,9 @@ export class Device extends EventEmitter {
1108
1101
  source,
1109
1102
  changedKeys: result.changedKeys,
1110
1103
  };
1111
- Log.debug('Device state updated', {
1104
+ Log.debug('Device state patch committed', {
1112
1105
  source,
1113
- revision: result.revision,
1114
- changedKeyCount: result.changedKeys.length,
1106
+ keys: result.changedKeys,
1115
1107
  });
1116
1108
  this.emit(DEVICE.STATE, this, event);
1117
1109
  if (result.state.protocol === 'V1') {
@@ -1132,17 +1124,12 @@ export class Device extends EventEmitter {
1132
1124
  return this.features;
1133
1125
  }
1134
1126
 
1135
- async ensureProtocolV2RuntimeContext(
1136
- timeoutMs?: number,
1137
- options?: { forceRefresh?: boolean }
1138
- ): Promise<ProtocolInfo> {
1127
+ async ensureProtocolV2RuntimeContext(timeoutMs?: number): Promise<ProtocolInfo> {
1139
1128
  const cachedProtocolInfo =
1140
- options?.forceRefresh === true
1141
- ? undefined
1142
- : this.protocolV2RuntimeContext ??
1143
- (!this.protocolV2StateNeedsReload
1144
- ? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
1145
- : undefined);
1129
+ this.protocolV2RuntimeContext ??
1130
+ (!this.protocolV2StateNeedsReload
1131
+ ? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
1132
+ : undefined);
1146
1133
  if (cachedProtocolInfo) {
1147
1134
  this.protocolV2RuntimeContext = cachedProtocolInfo;
1148
1135
  return cachedProtocolInfo;
@@ -1182,19 +1169,10 @@ export class Device extends EventEmitter {
1182
1169
  }
1183
1170
  }
1184
1171
 
1185
- async probeProtocolV2RuntimeState(
1186
- deviceInfo?: ProtocolV2DeviceInfo,
1187
- timeoutMs?: number,
1188
- options?: {
1189
- forceRuntimeContextRefresh?: boolean;
1190
- }
1191
- ) {
1192
- const protocolInfo = await this.ensureProtocolV2RuntimeContext(timeoutMs, {
1193
- forceRefresh: options?.forceRuntimeContextRefresh,
1194
- });
1172
+ async probeProtocolV2RuntimeState(deviceInfo?: ProtocolV2DeviceInfo, timeoutMs?: number) {
1173
+ const protocolInfo = await this.ensureProtocolV2RuntimeContext(timeoutMs);
1174
+ const runtimeMode = getProtocolV2RuntimeMode(protocolInfo);
1195
1175
  const runtimeDeviceInfo = deviceInfo ?? this.state?.raw?.protocolV2DeviceInfo;
1196
- const runtimeMode = getProtocolV2RuntimeMode(protocolInfo, runtimeDeviceInfo);
1197
- const legacyProtocolInfo = isLegacyProtocolV2ProtocolInfo(protocolInfo);
1198
1176
  const protocolV2DeviceType = runtimeDeviceInfo
1199
1177
  ? resolveProtocolV2DeviceIdentity(runtimeDeviceInfo.hw?.Device_type).deviceType
1200
1178
  : this.getCurrentDeviceType();
@@ -1208,9 +1186,10 @@ export class Device extends EventEmitter {
1208
1186
  'Protocol V2 romloader mode is only supported for Pro2 and Neo.'
1209
1187
  );
1210
1188
  }
1211
- const deviceStatusSupported =
1212
- legacyProtocolInfo ||
1213
- supportsProtocolV2Message(protocolInfo, PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE);
1189
+ const deviceStatusSupported = supportsProtocolV2Message(
1190
+ protocolInfo,
1191
+ PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE
1192
+ );
1214
1193
 
1215
1194
  if (runtimeMode === 'bootloader' || runtimeMode === 'romloader') {
1216
1195
  return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
@@ -1271,7 +1250,8 @@ export class Device extends EventEmitter {
1271
1250
  });
1272
1251
  }
1273
1252
 
1274
- private invalidateProtocolV2RuntimeState() {
1253
+ markTransportDisconnected() {
1254
+ this.deviceAcquired = false;
1275
1255
  if (!this.isProtocolV2()) return;
1276
1256
  this.protocolV2StateNeedsReload = true;
1277
1257
  this.protocolV2RuntimeContext = undefined;
@@ -1280,11 +1260,6 @@ export class Device extends EventEmitter {
1280
1260
  this.clearPreInitialized();
1281
1261
  }
1282
1262
 
1283
- markTransportDisconnected() {
1284
- this.deviceAcquired = false;
1285
- this.invalidateProtocolV2RuntimeState();
1286
- }
1287
-
1288
1263
  invalidateAfterWipe() {
1289
1264
  const deviceId = this.getCurrentDeviceId();
1290
1265
  if (deviceId) {
@@ -1294,7 +1269,10 @@ export class Device extends EventEmitter {
1294
1269
  if (this.originalDescriptor.path !== deviceId) {
1295
1270
  deviceWalletSessionStore.deleteDevice(this.originalDescriptor.path);
1296
1271
  }
1297
- this.invalidateProtocolV2RuntimeState();
1272
+ this.protocolV2StateNeedsReload = true;
1273
+ this.protocolV2RuntimeContext = undefined;
1274
+ this.protocolV2RuntimeContextPromise = undefined;
1275
+ this.protocolV2RuntimeContextRequestToken = undefined;
1298
1276
  }
1299
1277
 
1300
1278
  this.passphraseState = undefined;
@@ -1306,7 +1284,11 @@ export class Device extends EventEmitter {
1306
1284
  markProtocolV2Reboot(rebootType: DeviceRebootType) {
1307
1285
  if (!this.isProtocolV2()) return;
1308
1286
 
1309
- this.invalidateProtocolV2RuntimeState();
1287
+ this.protocolV2StateNeedsReload = true;
1288
+ this.protocolV2RuntimeContext = undefined;
1289
+ this.protocolV2RuntimeContextPromise = undefined;
1290
+ this.protocolV2RuntimeContextRequestToken = undefined;
1291
+ this.clearPreInitialized();
1310
1292
  let loaderMode: 'bootloader' | 'romloader' | undefined;
1311
1293
  if (rebootType === DeviceRebootType.Bootloader) {
1312
1294
  loaderMode = 'bootloader';
@@ -1380,10 +1362,6 @@ export class Device extends EventEmitter {
1380
1362
  if (device.features) {
1381
1363
  this._updateFeatures(device.features);
1382
1364
  }
1383
- // Adopting another Device instance's command channel also crosses an active
1384
- // link boundary. Renegotiate runtime metadata on that channel instead of
1385
- // retaining ProtocolInfo from the previous instance/session.
1386
- this.invalidateProtocolV2RuntimeState();
1387
1365
  }
1388
1366
 
1389
1367
  async run(fn?: () => Promise<void>, options?: RunOptions) {
@@ -304,6 +304,13 @@ export class DeviceCommands {
304
304
  const promise = this.transport.call(this.mainId, type, msg ?? {}, options) as any;
305
305
  this.callPromise = promise;
306
306
  const res = await promise;
307
+ if (!shouldReduceDebug) {
308
+ LogCore.debug(
309
+ '[DeviceCommands] [call] Received',
310
+ res.type,
311
+ getSafeTransportLogPayload(res.message, res.type)
312
+ );
313
+ }
307
314
  return res;
308
315
  } catch (error) {
309
316
  LogCore.debug('[DeviceCommands] [call] Received error', {
@@ -438,10 +445,13 @@ export class DeviceCommands {
438
445
  if (!shouldReduceDebugForCall(callType)) {
439
446
  Log.debug('_filterCommonTypes: ', {
440
447
  request: callType,
441
- response: {
442
- type: res.type,
443
- message: getSafeTransportLogPayload(res.message, res.type),
444
- },
448
+ response:
449
+ callType === 'DeviceFirmwareUpdateStatusGet'
450
+ ? {
451
+ type: res.type,
452
+ message: getSafeTransportLogPayload(res.message, res.type),
453
+ }
454
+ : res.type,
445
455
  });
446
456
  }
447
457
  } catch (error) {
@@ -218,13 +218,9 @@ export class DevicePool extends EventEmitter {
218
218
  * Device.getDeviceState skips unsupported status/settings calls in loader mode.
219
219
  */
220
220
  static async _refreshProtocolV2DiscoveryState(device: Device) {
221
- await device.getDeviceState({
222
- refreshSections: ['status'],
223
- });
221
+ await device.getDeviceState({ refreshSections: ['status'] });
224
222
  try {
225
- await device.getDeviceState({
226
- refreshSections: ['settings'],
227
- });
223
+ await device.getDeviceState({ refreshSections: ['settings'] });
228
224
  } catch (error) {
229
225
  Log.debug('Unable to refresh Protocol V2 device label during discovery', error);
230
226
  }
@@ -17,14 +17,6 @@ const LogLabelMethod: Set<string> = new Set([
17
17
  'fileRead',
18
18
  ]);
19
19
 
20
- // 资源上传参数可能包含很大的 Base64 字符串。这里按方法整段跳过,避免日志层
21
- // 递归复制和序列化这些数据;资源 API 与传输内容本身保持不变。
22
- const LogPayloadBlockMethod: Set<string> = new Set([
23
- 'deviceUploadNft',
24
- 'deviceUploadWallpaper',
25
- 'uploadPortfolio',
26
- ]);
27
-
28
20
  const SensitiveLogKeys: Set<string> = new Set([
29
21
  'devicestate',
30
22
  'entropy',
@@ -94,12 +86,7 @@ export function getLogBlockLabel(message: unknown): string | undefined {
94
86
  }
95
87
 
96
88
  export function getSafeLogPayload(value: unknown, blockLabel?: string): unknown {
97
- if (
98
- blockLabel &&
99
- (LogBlockEvent.has(blockLabel) ||
100
- LogPayloadBlockMethod.has(blockLabel) ||
101
- isSigningMethod(blockLabel))
102
- ) {
89
+ if (blockLabel && (LogBlockEvent.has(blockLabel) || isSigningMethod(blockLabel))) {
103
90
  return { method: blockLabel, payload: '[REDACTED]' };
104
91
  }
105
92
 
@@ -56,16 +56,6 @@ export const getProtocolV2SeType = (se?: DeviceSEInfo): string | null =>
56
56
 
57
57
  export type ProtocolV2RuntimeMode = 'normal' | 'bootloader' | 'romloader';
58
58
 
59
- export type ProtocolV2ProtocolInfo = ProtocolInfo & {
60
- /** Present only when hd-transport decoded the pre-build-fingerprint wire layout. */
61
- protobuf_definition?: string | null;
62
- };
63
-
64
- export const isLegacyProtocolV2ProtocolInfo = (
65
- protocolInfo: ProtocolInfo
66
- ): protocolInfo is ProtocolV2ProtocolInfo & { protobuf_definition: string | null } =>
67
- Object.prototype.hasOwnProperty.call(protocolInfo, 'protobuf_definition');
68
-
69
59
  /**
70
60
  * Protocol V2 fingerprints use:
71
61
  * <binary>__<version>__<commit>__<PROD|DEV>__<DEBUG|RELEASE>
@@ -97,18 +87,11 @@ export const parseProtocolV2BuildFingerprint = (
97
87
  };
98
88
 
99
89
  export const getProtocolV2RuntimeMode = (
100
- protocolInfo: ProtocolInfo,
101
- deviceInfo?: ProtocolV2DeviceInfo
90
+ protocolInfo: ProtocolInfo
102
91
  ): ProtocolV2RuntimeMode | undefined => {
103
92
  const binary = parseProtocolV2BuildFingerprint(protocolInfo.build_fingerprint)?.binary;
104
93
  if (binary === 'application') return 'normal';
105
- if (binary) return binary;
106
-
107
- if (isLegacyProtocolV2ProtocolInfo(protocolInfo) && !deviceInfo?.fw?.application) {
108
- if (deviceInfo?.fw?.romloader) return 'romloader';
109
- if (deviceInfo?.fw?.bootloader) return 'bootloader';
110
- }
111
- return undefined;
94
+ return binary;
112
95
  };
113
96
 
114
97
  // MessageType_DeviceStatusGet in the Protocol V2 protobuf registry.
@@ -7,7 +7,6 @@ export {
7
7
  getProtocolV2RuntimeMode,
8
8
  getProtocolV2SeState,
9
9
  getProtocolV2SeType,
10
- isLegacyProtocolV2ProtocolInfo,
11
10
  parseProtocolV2BuildFingerprint,
12
11
  requestProtocolV2ProtocolInfo,
13
12
  supportsProtocolV2Message,
@@ -19,7 +18,6 @@ export type {
19
18
  ProtocolV2SEInfo,
20
19
  ProtocolV2SeStateLabel,
21
20
  ProtocolV2RuntimeMode,
22
- ProtocolV2ProtocolInfo,
23
21
  } from './features';
24
22
  export * from './firmware';
25
23
  export * from './walletSession';
@@ -68,7 +68,7 @@ export declare function deviceUploadNft(
68
68
  export declare function uploadPortfolio(
69
69
  connectId: string,
70
70
  params: {
71
- packageBase64: string;
71
+ packageBytes: ArrayBuffer | Uint8Array | Blob;
72
72
  timeoutMs?: number | string;
73
73
  }
74
74
  ): Response<FileInfo & { portfolioUpdated: true }>;
@@ -75,7 +75,8 @@ export type SearchDevice = {
75
75
  serialNo?: string | null;
76
76
  /**
77
77
  * @deprecated Ambiguous legacy identifier. Use serialNo for hardware identity
78
- * and connectId for transport routing.
78
+ * and connectId for transport routing. Empty during BLE discovery until the
79
+ * physical serial number is available after initialization.
79
80
  */
80
81
  uuid: string;
81
82
  deviceId: string | null;
@@ -200,7 +200,7 @@ export const getAutoShutDownOptions = (
200
200
  return withNever([60_000, 120_000, 300_000, 600_000], protocol);
201
201
  case EDeviceType.Pro2:
202
202
  case EDeviceType.Neo:
203
- return withNever([60_000, 120_000, 300_000, 600_000], protocol);
203
+ return withNever([60_000, 120_000, 300_000, 600_000, 1_800_000], protocol);
204
204
  default:
205
205
  return [];
206
206
  }