@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.
- package/__tests__/DeviceCommands.test.ts +28 -100
- package/__tests__/device-lifecycle-events.test.ts +2 -113
- package/__tests__/device-pool-state.test.ts +0 -25
- package/__tests__/deviceSettings.test.ts +1 -0
- package/__tests__/deviceUploadNft.test.ts +4 -33
- package/__tests__/get-device-state.test.ts +13 -52
- package/__tests__/logBlockEvent.test.ts +1 -13
- package/__tests__/protocol-v2.test.ts +121 -758
- package/__tests__/refresh-device-state.test.ts +1 -3
- package/__tests__/search-devices.test.ts +38 -32
- package/dist/api/FirmwareUpdateV4.d.ts +3 -10
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -7
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +16 -12
- package/dist/index.js +201 -446
- package/dist/protocols/protocol-v2/features.d.ts +1 -9
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -1
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +0 -7
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +4 -6
- package/src/api/FirmwareUpdateV4.ts +174 -312
- package/src/api/SearchDevices.ts +2 -2
- package/src/api/UploadPortfolio.ts +2 -9
- package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
- package/src/api/ton/TonSignMessage.ts +3 -5
- package/src/core/index.ts +2 -6
- package/src/device/Device.ts +31 -53
- package/src/device/DeviceCommands.ts +14 -4
- package/src/device/DevicePool.ts +2 -6
- package/src/events/logBlockEvent.ts +1 -14
- package/src/protocols/protocol-v2/features.ts +2 -19
- package/src/protocols/protocol-v2/index.ts +0 -2
- package/src/types/api/protocolV2.ts +1 -1
- package/src/types/device.ts +2 -1
- package/src/utils/deviceSettings.ts +1 -1
- package/src/utils/pro2Nft.ts +8 -31
- package/__tests__/base64Data.test.ts +0 -70
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +0 -145
- package/__tests__/resourceBase64Boundary.test.ts +0 -48
- package/__tests__/ton-sign-message.test.ts +0 -84
- package/dist/api/helpers/base64Data.d.ts +0 -16
- package/dist/api/helpers/base64Data.d.ts.map +0 -1
- package/src/api/helpers/base64Data.ts +0 -85
package/src/api/SearchDevices.ts
CHANGED
|
@@ -42,8 +42,8 @@ export default class SearchDevices extends BaseMethod {
|
|
|
42
42
|
...device,
|
|
43
43
|
connectId: device.id,
|
|
44
44
|
serialNo: null,
|
|
45
|
-
//
|
|
46
|
-
uuid:
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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 = {
|
|
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
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) => {
|
package/src/device/Device.ts
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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
|
|
1104
|
+
Log.debug('Device state patch committed', {
|
|
1112
1105
|
source,
|
|
1113
|
-
|
|
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
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
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
|
-
|
|
1187
|
-
|
|
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
|
-
|
|
1213
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
443
|
-
|
|
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) {
|
package/src/device/DevicePool.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
71
|
+
packageBytes: ArrayBuffer | Uint8Array | Blob;
|
|
72
72
|
timeoutMs?: number | string;
|
|
73
73
|
}
|
|
74
74
|
): Response<FileInfo & { portfolioUpdated: true }>;
|
package/src/types/device.ts
CHANGED
|
@@ -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
|
}
|