@onekeyfe/hd-core 1.2.0-alpha.107 → 1.2.0-alpha.109
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 +140 -26
- package/__tests__/base64Data.test.ts +70 -0
- package/__tests__/device-lifecycle-events.test.ts +113 -2
- package/__tests__/device-pool-state.test.ts +25 -0
- package/__tests__/device-state-mapper.test.ts +4 -4
- package/__tests__/device-utils.test.ts +1 -1
- package/__tests__/deviceSettings.test.ts +0 -1
- package/__tests__/deviceUploadNft.test.ts +33 -4
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +125 -0
- package/__tests__/get-device-state.test.ts +60 -21
- package/__tests__/logBlockEvent.test.ts +13 -1
- package/__tests__/protocol-v2-resources.test.ts +8 -0
- package/__tests__/protocol-v2.test.ts +821 -227
- package/__tests__/refresh-device-state.test.ts +3 -1
- package/__tests__/resourceBase64Boundary.test.ts +48 -0
- package/__tests__/ton-sign-message.test.ts +84 -0
- package/dist/api/FirmwareUpdateV4.d.ts +7 -3
- 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/helpers/base64Data.d.ts +16 -0
- package/dist/api/helpers/base64Data.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +2 -3
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +2 -3
- 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 +7 -2
- 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 -34
- package/dist/index.js +506 -329
- package/dist/protocols/protocol-v2/features.d.ts +13 -5
- 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/protocols/protocol-v2/resources.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/settings.d.ts +4 -19
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +7 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +6 -4
- package/src/api/FirmwareUpdateV4.ts +307 -245
- package/src/api/UploadPortfolio.ts +9 -2
- package/src/api/helpers/base64Data.ts +85 -0
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
- package/src/api/protocol-v2/DeviceInfoGet.ts +3 -3
- package/src/api/protocol-v2/DeviceUploadNft.ts +56 -8
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +37 -18
- package/src/api/ton/TonSignMessage.ts +5 -3
- package/src/core/index.ts +6 -2
- package/src/data/messages/messages-protocol-v2.json +31 -14
- package/src/device/Device.ts +53 -31
- package/src/device/DeviceCommands.ts +4 -14
- package/src/device/DevicePool.ts +6 -2
- package/src/device/DeviceStateMapper.ts +15 -15
- package/src/deviceProfile/buildDeviceFeatures.ts +3 -3
- package/src/events/logBlockEvent.ts +14 -1
- package/src/protocols/protocol-v2/features.ts +22 -5
- package/src/protocols/protocol-v2/index.ts +2 -0
- package/src/protocols/protocol-v2/resources.ts +9 -46
- package/src/types/api/protocolV2.ts +1 -1
- package/src/types/settings.ts +4 -19
- package/src/utils/deviceSettings.ts +1 -1
- package/src/utils/pro2Nft.ts +31 -8
package/src/device/Device.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
62
62
|
type ProtocolV2RuntimeMode,
|
|
63
63
|
getProtocolV2RuntimeMode,
|
|
64
|
+
isLegacyProtocolV2ProtocolInfo,
|
|
64
65
|
requestProtocolV2DeviceInfo,
|
|
65
66
|
requestProtocolV2DeviceStatus,
|
|
66
67
|
requestProtocolV2ProtocolInfo,
|
|
@@ -485,6 +486,10 @@ export class Device extends EventEmitter {
|
|
|
485
486
|
}
|
|
486
487
|
|
|
487
488
|
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();
|
|
488
493
|
} catch (error) {
|
|
489
494
|
if (options?.forceProtocolDetection) {
|
|
490
495
|
this.originalDescriptor.protocolType = previousProtocol;
|
|
@@ -977,11 +982,7 @@ export class Device extends EventEmitter {
|
|
|
977
982
|
});
|
|
978
983
|
// The default request excludes SE/hash data and therefore uses basic scope.
|
|
979
984
|
// Full version and verification data require getDeviceState({ scope: 'firmware' }).
|
|
980
|
-
|
|
981
|
-
deviceInfo,
|
|
982
|
-
options?.protocolV2DeviceInfoTimeoutMs
|
|
983
|
-
);
|
|
984
|
-
Log.debug('Protocol V2 features:', features);
|
|
985
|
+
await this.probeProtocolV2RuntimeState(deviceInfo, options?.protocolV2DeviceInfoTimeoutMs);
|
|
985
986
|
} catch (error) {
|
|
986
987
|
Log.error('Protocol V2 initialization failed:', error);
|
|
987
988
|
throw error;
|
|
@@ -1053,7 +1054,13 @@ export class Device extends EventEmitter {
|
|
|
1053
1054
|
}
|
|
1054
1055
|
|
|
1055
1056
|
if (refresh.has('status') && !initializedWithDeviceInfo) {
|
|
1056
|
-
|
|
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
|
+
});
|
|
1057
1064
|
}
|
|
1058
1065
|
|
|
1059
1066
|
if (refresh.has('settings') && this.state?.status.mode === 'normal') {
|
|
@@ -1101,9 +1108,10 @@ export class Device extends EventEmitter {
|
|
|
1101
1108
|
source,
|
|
1102
1109
|
changedKeys: result.changedKeys,
|
|
1103
1110
|
};
|
|
1104
|
-
Log.debug('Device state
|
|
1111
|
+
Log.debug('Device state updated', {
|
|
1105
1112
|
source,
|
|
1106
|
-
|
|
1113
|
+
revision: result.revision,
|
|
1114
|
+
changedKeyCount: result.changedKeys.length,
|
|
1107
1115
|
});
|
|
1108
1116
|
this.emit(DEVICE.STATE, this, event);
|
|
1109
1117
|
if (result.state.protocol === 'V1') {
|
|
@@ -1124,12 +1132,17 @@ export class Device extends EventEmitter {
|
|
|
1124
1132
|
return this.features;
|
|
1125
1133
|
}
|
|
1126
1134
|
|
|
1127
|
-
async ensureProtocolV2RuntimeContext(
|
|
1135
|
+
async ensureProtocolV2RuntimeContext(
|
|
1136
|
+
timeoutMs?: number,
|
|
1137
|
+
options?: { forceRefresh?: boolean }
|
|
1138
|
+
): Promise<ProtocolInfo> {
|
|
1128
1139
|
const cachedProtocolInfo =
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1140
|
+
options?.forceRefresh === true
|
|
1141
|
+
? undefined
|
|
1142
|
+
: this.protocolV2RuntimeContext ??
|
|
1143
|
+
(!this.protocolV2StateNeedsReload
|
|
1144
|
+
? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
|
|
1145
|
+
: undefined);
|
|
1133
1146
|
if (cachedProtocolInfo) {
|
|
1134
1147
|
this.protocolV2RuntimeContext = cachedProtocolInfo;
|
|
1135
1148
|
return cachedProtocolInfo;
|
|
@@ -1169,10 +1182,19 @@ export class Device extends EventEmitter {
|
|
|
1169
1182
|
}
|
|
1170
1183
|
}
|
|
1171
1184
|
|
|
1172
|
-
async probeProtocolV2RuntimeState(
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
+
});
|
|
1175
1195
|
const runtimeDeviceInfo = deviceInfo ?? this.state?.raw?.protocolV2DeviceInfo;
|
|
1196
|
+
const runtimeMode = getProtocolV2RuntimeMode(protocolInfo, runtimeDeviceInfo);
|
|
1197
|
+
const legacyProtocolInfo = isLegacyProtocolV2ProtocolInfo(protocolInfo);
|
|
1176
1198
|
const protocolV2DeviceType = runtimeDeviceInfo
|
|
1177
1199
|
? resolveProtocolV2DeviceIdentity(runtimeDeviceInfo.hw?.Device_type).deviceType
|
|
1178
1200
|
: this.getCurrentDeviceType();
|
|
@@ -1186,10 +1208,9 @@ export class Device extends EventEmitter {
|
|
|
1186
1208
|
'Protocol V2 romloader mode is only supported for Pro2 and Neo.'
|
|
1187
1209
|
);
|
|
1188
1210
|
}
|
|
1189
|
-
const deviceStatusSupported =
|
|
1190
|
-
|
|
1191
|
-
PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE
|
|
1192
|
-
);
|
|
1211
|
+
const deviceStatusSupported =
|
|
1212
|
+
legacyProtocolInfo ||
|
|
1213
|
+
supportsProtocolV2Message(protocolInfo, PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE);
|
|
1193
1214
|
|
|
1194
1215
|
if (runtimeMode === 'bootloader' || runtimeMode === 'romloader') {
|
|
1195
1216
|
return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
|
|
@@ -1250,8 +1271,7 @@ export class Device extends EventEmitter {
|
|
|
1250
1271
|
});
|
|
1251
1272
|
}
|
|
1252
1273
|
|
|
1253
|
-
|
|
1254
|
-
this.deviceAcquired = false;
|
|
1274
|
+
private invalidateProtocolV2RuntimeState() {
|
|
1255
1275
|
if (!this.isProtocolV2()) return;
|
|
1256
1276
|
this.protocolV2StateNeedsReload = true;
|
|
1257
1277
|
this.protocolV2RuntimeContext = undefined;
|
|
@@ -1260,6 +1280,11 @@ export class Device extends EventEmitter {
|
|
|
1260
1280
|
this.clearPreInitialized();
|
|
1261
1281
|
}
|
|
1262
1282
|
|
|
1283
|
+
markTransportDisconnected() {
|
|
1284
|
+
this.deviceAcquired = false;
|
|
1285
|
+
this.invalidateProtocolV2RuntimeState();
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1263
1288
|
invalidateAfterWipe() {
|
|
1264
1289
|
const deviceId = this.getCurrentDeviceId();
|
|
1265
1290
|
if (deviceId) {
|
|
@@ -1269,10 +1294,7 @@ export class Device extends EventEmitter {
|
|
|
1269
1294
|
if (this.originalDescriptor.path !== deviceId) {
|
|
1270
1295
|
deviceWalletSessionStore.deleteDevice(this.originalDescriptor.path);
|
|
1271
1296
|
}
|
|
1272
|
-
this.
|
|
1273
|
-
this.protocolV2RuntimeContext = undefined;
|
|
1274
|
-
this.protocolV2RuntimeContextPromise = undefined;
|
|
1275
|
-
this.protocolV2RuntimeContextRequestToken = undefined;
|
|
1297
|
+
this.invalidateProtocolV2RuntimeState();
|
|
1276
1298
|
}
|
|
1277
1299
|
|
|
1278
1300
|
this.passphraseState = undefined;
|
|
@@ -1284,11 +1306,7 @@ export class Device extends EventEmitter {
|
|
|
1284
1306
|
markProtocolV2Reboot(rebootType: DeviceRebootType) {
|
|
1285
1307
|
if (!this.isProtocolV2()) return;
|
|
1286
1308
|
|
|
1287
|
-
this.
|
|
1288
|
-
this.protocolV2RuntimeContext = undefined;
|
|
1289
|
-
this.protocolV2RuntimeContextPromise = undefined;
|
|
1290
|
-
this.protocolV2RuntimeContextRequestToken = undefined;
|
|
1291
|
-
this.clearPreInitialized();
|
|
1309
|
+
this.invalidateProtocolV2RuntimeState();
|
|
1292
1310
|
let loaderMode: 'bootloader' | 'romloader' | undefined;
|
|
1293
1311
|
if (rebootType === DeviceRebootType.Bootloader) {
|
|
1294
1312
|
loaderMode = 'bootloader';
|
|
@@ -1362,6 +1380,10 @@ export class Device extends EventEmitter {
|
|
|
1362
1380
|
if (device.features) {
|
|
1363
1381
|
this._updateFeatures(device.features);
|
|
1364
1382
|
}
|
|
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();
|
|
1365
1387
|
}
|
|
1366
1388
|
|
|
1367
1389
|
async run(fn?: () => Promise<void>, options?: RunOptions) {
|
|
@@ -304,13 +304,6 @@ 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
|
-
}
|
|
314
307
|
return res;
|
|
315
308
|
} catch (error) {
|
|
316
309
|
LogCore.debug('[DeviceCommands] [call] Received error', {
|
|
@@ -445,13 +438,10 @@ export class DeviceCommands {
|
|
|
445
438
|
if (!shouldReduceDebugForCall(callType)) {
|
|
446
439
|
Log.debug('_filterCommonTypes: ', {
|
|
447
440
|
request: callType,
|
|
448
|
-
response:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
message: getSafeTransportLogPayload(res.message, res.type),
|
|
453
|
-
}
|
|
454
|
-
: res.type,
|
|
441
|
+
response: {
|
|
442
|
+
type: res.type,
|
|
443
|
+
message: getSafeTransportLogPayload(res.message, res.type),
|
|
444
|
+
},
|
|
455
445
|
});
|
|
456
446
|
}
|
|
457
447
|
} catch (error) {
|
package/src/device/DevicePool.ts
CHANGED
|
@@ -218,9 +218,13 @@ 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({
|
|
221
|
+
await device.getDeviceState({
|
|
222
|
+
refreshSections: ['status'],
|
|
223
|
+
});
|
|
222
224
|
try {
|
|
223
|
-
await device.getDeviceState({
|
|
225
|
+
await device.getDeviceState({
|
|
226
|
+
refreshSections: ['settings'],
|
|
227
|
+
});
|
|
224
228
|
} catch (error) {
|
|
225
229
|
Log.debug('Unable to refresh Protocol V2 device label during discovery', error);
|
|
226
230
|
}
|
|
@@ -267,11 +267,11 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
267
267
|
}
|
|
268
268
|
: { mode },
|
|
269
269
|
versions: definedEntries({
|
|
270
|
-
firmware: imageVersion(info.
|
|
271
|
-
applicationP1: imageVersion(info.
|
|
272
|
-
applicationP2: imageVersion(info.
|
|
273
|
-
bootloader: imageVersion(info.
|
|
274
|
-
board: imageVersion(info.
|
|
270
|
+
firmware: imageVersion(info.main_mcu?.application),
|
|
271
|
+
applicationP1: imageVersion(info.main_mcu?.application),
|
|
272
|
+
applicationP2: imageVersion(info.main_mcu?.application_data),
|
|
273
|
+
bootloader: imageVersion(info.main_mcu?.bootloader),
|
|
274
|
+
board: imageVersion(info.main_mcu?.romloader),
|
|
275
275
|
ble: imageVersion(info.coprocessor?.application),
|
|
276
276
|
se01: imageVersion(info.se1?.application),
|
|
277
277
|
se02: imageVersion(info.se2?.application),
|
|
@@ -283,16 +283,16 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
283
283
|
se04Boot: imageVersion(info.se4?.bootloader),
|
|
284
284
|
}),
|
|
285
285
|
verification: definedEntries({
|
|
286
|
-
firmwareBuildId: imageBuildId(info.
|
|
287
|
-
firmwareHash: imageHash(info.
|
|
288
|
-
applicationP1BuildId: imageBuildId(info.
|
|
289
|
-
applicationP1Hash: imageHash(info.
|
|
290
|
-
applicationP2BuildId: imageBuildId(info.
|
|
291
|
-
applicationP2Hash: imageHash(info.
|
|
292
|
-
bootloaderBuildId: imageBuildId(info.
|
|
293
|
-
bootloaderHash: imageHash(info.
|
|
294
|
-
boardBuildId: imageBuildId(info.
|
|
295
|
-
boardHash: imageHash(info.
|
|
286
|
+
firmwareBuildId: imageBuildId(info.main_mcu?.application),
|
|
287
|
+
firmwareHash: imageHash(info.main_mcu?.application),
|
|
288
|
+
applicationP1BuildId: imageBuildId(info.main_mcu?.application),
|
|
289
|
+
applicationP1Hash: imageHash(info.main_mcu?.application),
|
|
290
|
+
applicationP2BuildId: imageBuildId(info.main_mcu?.application_data),
|
|
291
|
+
applicationP2Hash: imageHash(info.main_mcu?.application_data),
|
|
292
|
+
bootloaderBuildId: imageBuildId(info.main_mcu?.bootloader),
|
|
293
|
+
bootloaderHash: imageHash(info.main_mcu?.bootloader),
|
|
294
|
+
boardBuildId: imageBuildId(info.main_mcu?.romloader),
|
|
295
|
+
boardHash: imageHash(info.main_mcu?.romloader),
|
|
296
296
|
bleBuildId: imageBuildId(info.coprocessor?.application),
|
|
297
297
|
bleHash: imageHash(info.coprocessor?.application),
|
|
298
298
|
se01BuildId: imageBuildId(info.se1?.application),
|
|
@@ -216,9 +216,9 @@ export const buildProtocolV2FeaturesPayload = ({
|
|
|
216
216
|
runtimeMode?: ProtocolV2RuntimeMode;
|
|
217
217
|
}): Features => {
|
|
218
218
|
const info = deviceInfo;
|
|
219
|
-
const fwApplication = info?.
|
|
220
|
-
const fwBootloader = info?.
|
|
221
|
-
const fwBoard = info?.
|
|
219
|
+
const fwApplication = info?.main_mcu?.application;
|
|
220
|
+
const fwBootloader = info?.main_mcu?.bootloader;
|
|
221
|
+
const fwBoard = info?.main_mcu?.romloader;
|
|
222
222
|
const bleApplication = info?.coprocessor?.application;
|
|
223
223
|
const status = deviceStatus;
|
|
224
224
|
const incomingSerialNo = info?.hw?.serial_no;
|
|
@@ -17,6 +17,14 @@ 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
|
+
|
|
20
28
|
const SensitiveLogKeys: Set<string> = new Set([
|
|
21
29
|
'devicestate',
|
|
22
30
|
'entropy',
|
|
@@ -86,7 +94,12 @@ export function getLogBlockLabel(message: unknown): string | undefined {
|
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
export function getSafeLogPayload(value: unknown, blockLabel?: string): unknown {
|
|
89
|
-
if (
|
|
97
|
+
if (
|
|
98
|
+
blockLabel &&
|
|
99
|
+
(LogBlockEvent.has(blockLabel) ||
|
|
100
|
+
LogPayloadBlockMethod.has(blockLabel) ||
|
|
101
|
+
isSigningMethod(blockLabel))
|
|
102
|
+
) {
|
|
90
103
|
return { method: blockLabel, payload: '[REDACTED]' };
|
|
91
104
|
}
|
|
92
105
|
|
|
@@ -56,6 +56,16 @@ 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
|
+
|
|
59
69
|
/**
|
|
60
70
|
* Protocol V2 fingerprints use:
|
|
61
71
|
* <binary>__<version>__<commit>__<PROD|DEV>__<DEBUG|RELEASE>
|
|
@@ -87,11 +97,18 @@ export const parseProtocolV2BuildFingerprint = (
|
|
|
87
97
|
};
|
|
88
98
|
|
|
89
99
|
export const getProtocolV2RuntimeMode = (
|
|
90
|
-
protocolInfo: ProtocolInfo
|
|
100
|
+
protocolInfo: ProtocolInfo,
|
|
101
|
+
deviceInfo?: ProtocolV2DeviceInfo
|
|
91
102
|
): ProtocolV2RuntimeMode | undefined => {
|
|
92
103
|
const binary = parseProtocolV2BuildFingerprint(protocolInfo.build_fingerprint)?.binary;
|
|
93
104
|
if (binary === 'application') return 'normal';
|
|
94
|
-
return binary;
|
|
105
|
+
if (binary) return binary;
|
|
106
|
+
|
|
107
|
+
if (isLegacyProtocolV2ProtocolInfo(protocolInfo) && !deviceInfo?.main_mcu?.application) {
|
|
108
|
+
if (deviceInfo?.main_mcu?.romloader) return 'romloader';
|
|
109
|
+
if (deviceInfo?.main_mcu?.bootloader) return 'bootloader';
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
95
112
|
};
|
|
96
113
|
|
|
97
114
|
// MessageType_DeviceStatusGet in the Protocol V2 protobuf registry.
|
|
@@ -105,7 +122,7 @@ export const supportsProtocolV2Message = (
|
|
|
105
122
|
export const PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST = {
|
|
106
123
|
targets: {
|
|
107
124
|
hw: true,
|
|
108
|
-
|
|
125
|
+
main_mcu: true,
|
|
109
126
|
coprocessor: true,
|
|
110
127
|
},
|
|
111
128
|
types: {
|
|
@@ -121,7 +138,7 @@ export const PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST = {
|
|
|
121
138
|
export const PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST = {
|
|
122
139
|
targets: {
|
|
123
140
|
hw: true,
|
|
124
|
-
|
|
141
|
+
main_mcu: true,
|
|
125
142
|
coprocessor: true,
|
|
126
143
|
se1: true,
|
|
127
144
|
se2: true,
|
|
@@ -137,7 +154,7 @@ export const PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST = {
|
|
|
137
154
|
export const PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST = {
|
|
138
155
|
targets: {
|
|
139
156
|
hw: true,
|
|
140
|
-
|
|
157
|
+
main_mcu: true,
|
|
141
158
|
coprocessor: true,
|
|
142
159
|
se1: true,
|
|
143
160
|
se2: true,
|
|
@@ -7,6 +7,7 @@ export {
|
|
|
7
7
|
getProtocolV2RuntimeMode,
|
|
8
8
|
getProtocolV2SeState,
|
|
9
9
|
getProtocolV2SeType,
|
|
10
|
+
isLegacyProtocolV2ProtocolInfo,
|
|
10
11
|
parseProtocolV2BuildFingerprint,
|
|
11
12
|
requestProtocolV2ProtocolInfo,
|
|
12
13
|
supportsProtocolV2Message,
|
|
@@ -18,6 +19,7 @@ export type {
|
|
|
18
19
|
ProtocolV2SEInfo,
|
|
19
20
|
ProtocolV2SeStateLabel,
|
|
20
21
|
ProtocolV2RuntimeMode,
|
|
22
|
+
ProtocolV2ProtocolInfo,
|
|
21
23
|
} from './features';
|
|
22
24
|
export * from './firmware';
|
|
23
25
|
export * from './walletSession';
|
|
@@ -107,10 +107,10 @@ function parseProtocolV2ResourceManifestFile(
|
|
|
107
107
|
}
|
|
108
108
|
const file = value as Partial<IProtocolV2ResourceManifestFile>;
|
|
109
109
|
const archivePath = assertManifestRelativePath(file.archive_path, `files[${index}].archive_path`);
|
|
110
|
-
const originalName =
|
|
111
|
-
file.original_name
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
const originalName =
|
|
111
|
+
file.original_name === undefined
|
|
112
|
+
? archivePath.split('/').pop() ?? archivePath
|
|
113
|
+
: assertManifestRelativePath(file.original_name, `files[${index}].original_name`);
|
|
114
114
|
if (originalName.includes('/')) {
|
|
115
115
|
throw new Error(`Invalid Pro2 resource manifest files[${index}].original_name`);
|
|
116
116
|
}
|
|
@@ -122,15 +122,6 @@ function parseProtocolV2ResourceManifestFile(
|
|
|
122
122
|
throw new Error(`Invalid Pro2 resource manifest files[${index}].size`);
|
|
123
123
|
}
|
|
124
124
|
const digest = normalizeHex(file.sha256, SHA256_HEX_LENGTH, `files[${index}].sha256`);
|
|
125
|
-
if (file.signed !== true) {
|
|
126
|
-
throw new Error(`Invalid Pro2 resource manifest files[${index}].signed`);
|
|
127
|
-
}
|
|
128
|
-
if (file.sig_algo !== 'ed25519' && file.sig_algo !== 'mldsa65') {
|
|
129
|
-
throw new Error(`Invalid Pro2 resource manifest files[${index}].sig_algo`);
|
|
130
|
-
}
|
|
131
|
-
if (file.payload_version !== null && typeof file.payload_version !== 'string') {
|
|
132
|
-
throw new Error(`Invalid Pro2 resource manifest files[${index}].payload_version`);
|
|
133
|
-
}
|
|
134
125
|
if (!archivePath.endsWith('.okpkg') || !originalName.endsWith('.okpkg')) {
|
|
135
126
|
throw new Error(`Invalid Pro2 resource manifest files[${index}] package extension`);
|
|
136
127
|
}
|
|
@@ -140,9 +131,9 @@ function parseProtocolV2ResourceManifestFile(
|
|
|
140
131
|
device_path: devicePath,
|
|
141
132
|
size: Number(file.size),
|
|
142
133
|
sha256: digest,
|
|
143
|
-
signed:
|
|
144
|
-
sig_algo: file.sig_algo,
|
|
145
|
-
payload_version: file.payload_version
|
|
134
|
+
...(file.signed === undefined ? {} : { signed: file.signed }),
|
|
135
|
+
...(file.sig_algo === undefined ? {} : { sig_algo: file.sig_algo }),
|
|
136
|
+
...(file.payload_version === undefined ? {} : { payload_version: file.payload_version }),
|
|
146
137
|
};
|
|
147
138
|
}
|
|
148
139
|
|
|
@@ -151,15 +142,8 @@ export function parseProtocolV2ResourceManifest(value: unknown): IProtocolV2Reso
|
|
|
151
142
|
throw new Error('Invalid Pro2 resource manifest');
|
|
152
143
|
}
|
|
153
144
|
const manifest = value as Partial<IProtocolV2ResourceManifest>;
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
manifest.variant !== 'resource' ||
|
|
157
|
-
manifest.device_root !== 'vol0:' ||
|
|
158
|
-
manifest.restore_mode !== 'bootloader_update' ||
|
|
159
|
-
!Array.isArray(manifest.trees) ||
|
|
160
|
-
!Array.isArray(manifest.files)
|
|
161
|
-
) {
|
|
162
|
-
throw new Error('Invalid Pro2 resource manifest contract');
|
|
145
|
+
if (!Array.isArray(manifest.files)) {
|
|
146
|
+
throw new Error('Invalid Pro2 resource manifest files');
|
|
163
147
|
}
|
|
164
148
|
const files = manifest.files.map(parseProtocolV2ResourceManifestFile);
|
|
165
149
|
const devicePaths = new Set(files.map(file => file.device_path));
|
|
@@ -174,27 +158,6 @@ export function parseProtocolV2ResourceManifest(value: unknown): IProtocolV2Reso
|
|
|
174
158
|
throw new Error('Invalid Pro2 resource manifest file set');
|
|
175
159
|
}
|
|
176
160
|
return {
|
|
177
|
-
schema: 1,
|
|
178
|
-
artifact_name: assertManifestString(manifest.artifact_name, 'artifact_name'),
|
|
179
|
-
release_name: assertManifestString(manifest.release_name, 'release_name'),
|
|
180
|
-
variant: 'resource',
|
|
181
|
-
commit: assertManifestString(manifest.commit, 'commit'),
|
|
182
|
-
short_sha: assertManifestString(manifest.short_sha, 'short_sha'),
|
|
183
|
-
timestamp_utc: assertManifestString(manifest.timestamp_utc, 'timestamp_utc'),
|
|
184
|
-
core_version: assertManifestString(manifest.core_version, 'core_version'),
|
|
185
|
-
key_set: assertManifestString(manifest.key_set, 'key_set'),
|
|
186
|
-
device_root: 'vol0:',
|
|
187
|
-
restore_mode: 'bootloader_update',
|
|
188
|
-
trees: manifest.trees.map((tree, index) => {
|
|
189
|
-
if (!tree || typeof tree !== 'object') {
|
|
190
|
-
throw new Error(`Invalid Pro2 resource manifest trees[${index}]`);
|
|
191
|
-
}
|
|
192
|
-
const item = tree as { path?: unknown; device?: unknown };
|
|
193
|
-
return {
|
|
194
|
-
path: assertManifestRelativePath(item.path, `trees[${index}].path`),
|
|
195
|
-
device: assertManifestString(item.device, `trees[${index}].device`),
|
|
196
|
-
};
|
|
197
|
-
}),
|
|
198
161
|
files,
|
|
199
162
|
};
|
|
200
163
|
}
|
|
@@ -68,7 +68,7 @@ export declare function deviceUploadNft(
|
|
|
68
68
|
export declare function uploadPortfolio(
|
|
69
69
|
connectId: string,
|
|
70
70
|
params: {
|
|
71
|
-
|
|
71
|
+
packageBase64: string;
|
|
72
72
|
timeoutMs?: number | string;
|
|
73
73
|
}
|
|
74
74
|
): Response<FileInfo & { portfolioUpdated: true }>;
|
package/src/types/settings.ts
CHANGED
|
@@ -76,31 +76,16 @@ export type IProtocolV2Resources = {
|
|
|
76
76
|
|
|
77
77
|
export type IProtocolV2ResourceManifestFile = {
|
|
78
78
|
archive_path: string;
|
|
79
|
-
original_name
|
|
79
|
+
original_name?: string;
|
|
80
80
|
device_path: string;
|
|
81
81
|
size: number;
|
|
82
82
|
sha256: string;
|
|
83
|
-
signed
|
|
84
|
-
sig_algo
|
|
85
|
-
payload_version
|
|
83
|
+
signed?: boolean;
|
|
84
|
+
sig_algo?: string;
|
|
85
|
+
payload_version?: string | null;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
export type IProtocolV2ResourceManifest = {
|
|
89
|
-
schema: 1;
|
|
90
|
-
artifact_name: string;
|
|
91
|
-
release_name: string;
|
|
92
|
-
variant: 'resource';
|
|
93
|
-
commit: string;
|
|
94
|
-
short_sha: string;
|
|
95
|
-
timestamp_utc: string;
|
|
96
|
-
core_version: string;
|
|
97
|
-
key_set: string;
|
|
98
|
-
device_root: 'vol0:';
|
|
99
|
-
restore_mode: 'bootloader_update';
|
|
100
|
-
trees: Array<{
|
|
101
|
-
path: string;
|
|
102
|
-
device: string;
|
|
103
|
-
}>;
|
|
104
89
|
files: IProtocolV2ResourceManifestFile[];
|
|
105
90
|
};
|
|
106
91
|
|
|
@@ -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
|
|
203
|
+
return withNever([60_000, 120_000, 300_000, 600_000], protocol);
|
|
204
204
|
default:
|
|
205
205
|
return [];
|
|
206
206
|
}
|
package/src/utils/pro2Nft.ts
CHANGED
|
@@ -109,6 +109,34 @@ export function buildPro2NftBundle(options: {
|
|
|
109
109
|
const { image, thumbnail, title, subtitle, timestampMs } = options;
|
|
110
110
|
assertImage('image', image, PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT);
|
|
111
111
|
assertImage('thumbnail', thumbnail, PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT);
|
|
112
|
+
const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
|
|
113
|
+
const encodedThumbnail = encodePro2Image({
|
|
114
|
+
...thumbnail,
|
|
115
|
+
alphaMode: 'black-background',
|
|
116
|
+
}).data;
|
|
117
|
+
return buildPro2NftBundleFromEncodedImages({
|
|
118
|
+
image: encodedImage,
|
|
119
|
+
thumbnail: encodedThumbnail,
|
|
120
|
+
title,
|
|
121
|
+
subtitle,
|
|
122
|
+
timestampMs,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function buildPro2NftBundleFromEncodedImages(options: {
|
|
127
|
+
image: Uint8Array;
|
|
128
|
+
thumbnail: Uint8Array;
|
|
129
|
+
title: string;
|
|
130
|
+
subtitle: string;
|
|
131
|
+
timestampMs: number;
|
|
132
|
+
}): Pro2NftBundle {
|
|
133
|
+
const { image, thumbnail, title, subtitle, timestampMs } = options;
|
|
134
|
+
if (!(image instanceof Uint8Array) || image.byteLength === 0) {
|
|
135
|
+
throw invalidParameter('Parameter [image] must contain encoded NFT image data.');
|
|
136
|
+
}
|
|
137
|
+
if (!(thumbnail instanceof Uint8Array) || thumbnail.byteLength === 0) {
|
|
138
|
+
throw invalidParameter('Parameter [thumbnail] must contain encoded NFT thumbnail data.');
|
|
139
|
+
}
|
|
112
140
|
const titleLength = typeof title === 'string' ? utf8Length(title) : 0;
|
|
113
141
|
const subtitleLength =
|
|
114
142
|
typeof subtitle === 'string' ? utf8Length(subtitle) : Number.POSITIVE_INFINITY;
|
|
@@ -122,21 +150,16 @@ export function buildPro2NftBundle(options: {
|
|
|
122
150
|
throw invalidParameter('Parameter [timestampMs] must be a positive safe integer.');
|
|
123
151
|
}
|
|
124
152
|
|
|
125
|
-
const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
|
|
126
|
-
const encodedThumbnail = encodePro2Image({
|
|
127
|
-
...thumbnail,
|
|
128
|
-
alphaMode: 'black-background',
|
|
129
|
-
}).data;
|
|
130
153
|
const metadata = new TextEncoder().encode(JSON.stringify({ title, subtitle }));
|
|
131
154
|
if (metadata.byteLength === 0 || metadata.byteLength > 512) {
|
|
132
155
|
throw invalidParameter('Pro2 NFT metadata must contain 1 to 512 UTF-8 bytes.');
|
|
133
156
|
}
|
|
134
157
|
|
|
135
|
-
const hash8 = bytesToHex(blake2s(
|
|
158
|
+
const hash8 = bytesToHex(blake2s(image)).slice(0, 8);
|
|
136
159
|
return {
|
|
137
160
|
basename: `nft-${hash8}-${timestampMs}`,
|
|
138
|
-
image
|
|
139
|
-
thumbnail
|
|
161
|
+
image,
|
|
162
|
+
thumbnail,
|
|
140
163
|
metadata,
|
|
141
164
|
};
|
|
142
165
|
}
|