@onekeyfe/hd-core 1.2.0-alpha.105 → 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 +2 -10
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/device-lifecycle-events.test.ts +2 -113
- package/__tests__/device-pool-state.test.ts +0 -29
- package/__tests__/deviceUploadNft.test.ts +4 -33
- package/__tests__/get-device-state.test.ts +13 -52
- package/__tests__/protocol-v2.test.ts +18 -330
- package/__tests__/refresh-device-state.test.ts +1 -4
- package/__tests__/search-devices.test.ts +38 -34
- package/dist/api/FirmwareUpdateV4.d.ts +0 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.d.ts.map +1 -1
- package/dist/api/SearchDevices.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/firmware/protocolV2Release.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 -10
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/index.d.ts +16 -18
- package/dist/index.js +99 -277
- package/dist/protocols/protocol-v2/features.d.ts +0 -8
- 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/getDeviceState.d.ts +0 -1
- package/dist/types/api/getDeviceState.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 +24 -72
- package/src/api/GetDeviceState.ts +0 -1
- package/src/api/SearchDevices.ts +2 -4
- package/src/api/UploadPortfolio.ts +2 -9
- package/src/api/firmware/protocolV2Release.ts +0 -1
- 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 +0 -4
- package/src/device/Device.ts +31 -90
- package/src/device/DevicePool.ts +5 -17
- package/src/protocols/protocol-v2/features.ts +0 -10
- package/src/protocols/protocol-v2/index.ts +0 -2
- package/src/types/api/getDeviceState.ts +0 -2
- package/src/types/api/protocolV2.ts +1 -1
- package/src/types/device.ts +2 -1
- package/src/utils/pro2Nft.ts +8 -31
- package/__tests__/base64Data.test.ts +0 -70
- package/__tests__/protocol-v2-legacy-recovery.test.ts +0 -29
- 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/dist/core/protocolV2LegacyRecovery.d.ts +0 -5
- package/dist/core/protocolV2LegacyRecovery.d.ts.map +0 -1
- package/src/api/helpers/base64Data.ts +0 -85
- package/src/core/protocolV2LegacyRecovery.ts +0 -12
package/src/core/index.ts
CHANGED
|
@@ -64,7 +64,6 @@ import {
|
|
|
64
64
|
isProtocolV2UiEnabled,
|
|
65
65
|
} from '../protocols/protocol-v2/uiInteraction';
|
|
66
66
|
import { createUiProgressMessageFilter } from '../utils/uiProgressThrottle';
|
|
67
|
-
import { isLegacyProtocolV2FirmwareRecoveryMethod } from './protocolV2LegacyRecovery';
|
|
68
67
|
|
|
69
68
|
import type { ConnectSettings, Features, KnownDevice } from '../types';
|
|
70
69
|
import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
|
|
@@ -110,9 +109,6 @@ const parseInitOptions = (method?: BaseMethod): InitOptions => ({
|
|
|
110
109
|
connectProtocol: method?.payload.connectProtocol,
|
|
111
110
|
forceProtocolDetection: method?.payload.forceProtocolDetection,
|
|
112
111
|
protocolV2DeviceInfoTimeoutMs: method?.payload.protocolV2DeviceInfoTimeoutMs,
|
|
113
|
-
...(isLegacyProtocolV2FirmwareRecoveryMethod(method)
|
|
114
|
-
? { allowLegacyProtocolV2ProtocolInfo: true }
|
|
115
|
-
: {}),
|
|
116
112
|
});
|
|
117
113
|
|
|
118
114
|
let _core: Core | undefined;
|
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,
|
|
@@ -100,8 +99,6 @@ export type InitOptions = {
|
|
|
100
99
|
protocolV2DeviceInfoTimeoutMs?: number;
|
|
101
100
|
/** Refresh Protocol V2 runtime state before returning discovery results. */
|
|
102
101
|
refreshRuntimeState?: boolean;
|
|
103
|
-
/** Recovery-only compatibility used by discovery and Protocol V2 firmware updates. */
|
|
104
|
-
allowLegacyProtocolV2ProtocolInfo?: boolean;
|
|
105
102
|
/**
|
|
106
103
|
* Protocol V1 Initialize response timeout override. Reboot-wait polling passes a
|
|
107
104
|
* short value so an unanswered probe settles before the next poll tick.
|
|
@@ -488,10 +485,6 @@ export class Device extends EventEmitter {
|
|
|
488
485
|
}
|
|
489
486
|
|
|
490
487
|
this.commands = new DeviceCommands(this, this.mainId ?? '');
|
|
491
|
-
// Protocol V2 runtime metadata belongs to one active transport link. A
|
|
492
|
-
// successful acquire creates a fresh link/session, so never carry cached
|
|
493
|
-
// ProtocolInfo or pre-initialize state across that boundary.
|
|
494
|
-
this.invalidateProtocolV2RuntimeState();
|
|
495
488
|
} catch (error) {
|
|
496
489
|
if (options?.forceProtocolDetection) {
|
|
497
490
|
this.originalDescriptor.protocolType = previousProtocol;
|
|
@@ -984,16 +977,10 @@ export class Device extends EventEmitter {
|
|
|
984
977
|
});
|
|
985
978
|
// The default request excludes SE/hash data and therefore uses basic scope.
|
|
986
979
|
// Full version and verification data require getDeviceState({ scope: 'firmware' }).
|
|
987
|
-
const features =
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
{ allowLegacyProtocolV2ProtocolInfo: true }
|
|
992
|
-
)
|
|
993
|
-
: await this.probeProtocolV2RuntimeState(
|
|
994
|
-
deviceInfo,
|
|
995
|
-
options?.protocolV2DeviceInfoTimeoutMs
|
|
996
|
-
);
|
|
980
|
+
const features = await this.probeProtocolV2RuntimeState(
|
|
981
|
+
deviceInfo,
|
|
982
|
+
options?.protocolV2DeviceInfoTimeoutMs
|
|
983
|
+
);
|
|
997
984
|
Log.debug('Protocol V2 features:', features);
|
|
998
985
|
} catch (error) {
|
|
999
986
|
Log.error('Protocol V2 initialization failed:', error);
|
|
@@ -1030,13 +1017,7 @@ export class Device extends EventEmitter {
|
|
|
1030
1017
|
commands: this.commands,
|
|
1031
1018
|
request: getProtocolV2DeviceInfoRequest(),
|
|
1032
1019
|
});
|
|
1033
|
-
|
|
1034
|
-
await this.probeProtocolV2RuntimeState(deviceInfo, undefined, {
|
|
1035
|
-
allowLegacyProtocolV2ProtocolInfo: true,
|
|
1036
|
-
});
|
|
1037
|
-
} else {
|
|
1038
|
-
await this.probeProtocolV2RuntimeState(deviceInfo);
|
|
1039
|
-
}
|
|
1020
|
+
await this.probeProtocolV2RuntimeState(deviceInfo);
|
|
1040
1021
|
refreshedDeviceInfo = deviceInfo;
|
|
1041
1022
|
initializedWithDeviceInfo = true;
|
|
1042
1023
|
} else {
|
|
@@ -1072,16 +1053,7 @@ export class Device extends EventEmitter {
|
|
|
1072
1053
|
}
|
|
1073
1054
|
|
|
1074
1055
|
if (refresh.has('status') && !initializedWithDeviceInfo) {
|
|
1075
|
-
|
|
1076
|
-
await this.probeProtocolV2RuntimeState(refreshedDeviceInfo, undefined, {
|
|
1077
|
-
// Loader firmware does not support DeviceStatusGet. Renegotiate ProtocolInfo
|
|
1078
|
-
// during an explicit refresh so a device rebooted into application firmware
|
|
1079
|
-
// can leave the cached loader state.
|
|
1080
|
-
forceRuntimeContextRefresh: cachedMode === 'bootloader' || cachedMode === 'romloader',
|
|
1081
|
-
...(params.allowLegacyProtocolV2ProtocolInfo
|
|
1082
|
-
? { allowLegacyProtocolV2ProtocolInfo: true }
|
|
1083
|
-
: {}),
|
|
1084
|
-
});
|
|
1056
|
+
await this.probeProtocolV2RuntimeState(refreshedDeviceInfo);
|
|
1085
1057
|
}
|
|
1086
1058
|
|
|
1087
1059
|
if (refresh.has('settings') && this.state?.status.mode === 'normal') {
|
|
@@ -1152,39 +1124,19 @@ export class Device extends EventEmitter {
|
|
|
1152
1124
|
return this.features;
|
|
1153
1125
|
}
|
|
1154
1126
|
|
|
1155
|
-
async ensureProtocolV2RuntimeContext(
|
|
1156
|
-
timeoutMs?: number,
|
|
1157
|
-
options?: {
|
|
1158
|
-
forceRefresh?: boolean;
|
|
1159
|
-
allowLegacyProtocolV2ProtocolInfo?: boolean;
|
|
1160
|
-
}
|
|
1161
|
-
): Promise<ProtocolInfo> {
|
|
1162
|
-
const assertProtocolInfoAllowed = (protocolInfo: ProtocolInfo) => {
|
|
1163
|
-
if (
|
|
1164
|
-
isLegacyProtocolV2ProtocolInfo(protocolInfo) &&
|
|
1165
|
-
options?.allowLegacyProtocolV2ProtocolInfo !== true
|
|
1166
|
-
) {
|
|
1167
|
-
throw ERRORS.TypedError(
|
|
1168
|
-
HardwareErrorCode.DeviceInitializeFailed,
|
|
1169
|
-
'Legacy Protocol V2 ProtocolInfo is supported only during device discovery and firmware update.'
|
|
1170
|
-
);
|
|
1171
|
-
}
|
|
1172
|
-
return protocolInfo;
|
|
1173
|
-
};
|
|
1127
|
+
async ensureProtocolV2RuntimeContext(timeoutMs?: number): Promise<ProtocolInfo> {
|
|
1174
1128
|
const cachedProtocolInfo =
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
|
|
1180
|
-
: undefined);
|
|
1129
|
+
this.protocolV2RuntimeContext ??
|
|
1130
|
+
(!this.protocolV2StateNeedsReload
|
|
1131
|
+
? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
|
|
1132
|
+
: undefined);
|
|
1181
1133
|
if (cachedProtocolInfo) {
|
|
1182
1134
|
this.protocolV2RuntimeContext = cachedProtocolInfo;
|
|
1183
|
-
return
|
|
1135
|
+
return cachedProtocolInfo;
|
|
1184
1136
|
}
|
|
1185
1137
|
|
|
1186
1138
|
if (this.protocolV2RuntimeContextPromise) {
|
|
1187
|
-
return
|
|
1139
|
+
return this.protocolV2RuntimeContextPromise;
|
|
1188
1140
|
}
|
|
1189
1141
|
|
|
1190
1142
|
const requestToken = {};
|
|
@@ -1206,7 +1158,7 @@ export class Device extends EventEmitter {
|
|
|
1206
1158
|
this.protocolV2RuntimeContextPromise = pendingRequest;
|
|
1207
1159
|
|
|
1208
1160
|
try {
|
|
1209
|
-
return
|
|
1161
|
+
return await pendingRequest;
|
|
1210
1162
|
} finally {
|
|
1211
1163
|
if (this.protocolV2RuntimeContextPromise === pendingRequest) {
|
|
1212
1164
|
this.protocolV2RuntimeContextPromise = undefined;
|
|
@@ -1217,20 +1169,9 @@ export class Device extends EventEmitter {
|
|
|
1217
1169
|
}
|
|
1218
1170
|
}
|
|
1219
1171
|
|
|
1220
|
-
async probeProtocolV2RuntimeState(
|
|
1221
|
-
|
|
1222
|
-
timeoutMs?: number,
|
|
1223
|
-
options?: {
|
|
1224
|
-
forceRuntimeContextRefresh?: boolean;
|
|
1225
|
-
allowLegacyProtocolV2ProtocolInfo?: boolean;
|
|
1226
|
-
}
|
|
1227
|
-
) {
|
|
1228
|
-
const protocolInfo = await this.ensureProtocolV2RuntimeContext(timeoutMs, {
|
|
1229
|
-
forceRefresh: options?.forceRuntimeContextRefresh,
|
|
1230
|
-
allowLegacyProtocolV2ProtocolInfo: options?.allowLegacyProtocolV2ProtocolInfo,
|
|
1231
|
-
});
|
|
1172
|
+
async probeProtocolV2RuntimeState(deviceInfo?: ProtocolV2DeviceInfo, timeoutMs?: number) {
|
|
1173
|
+
const protocolInfo = await this.ensureProtocolV2RuntimeContext(timeoutMs);
|
|
1232
1174
|
const runtimeMode = getProtocolV2RuntimeMode(protocolInfo);
|
|
1233
|
-
const legacyProtocolInfo = isLegacyProtocolV2ProtocolInfo(protocolInfo);
|
|
1234
1175
|
const runtimeDeviceInfo = deviceInfo ?? this.state?.raw?.protocolV2DeviceInfo;
|
|
1235
1176
|
const protocolV2DeviceType = runtimeDeviceInfo
|
|
1236
1177
|
? resolveProtocolV2DeviceIdentity(runtimeDeviceInfo.hw?.Device_type).deviceType
|
|
@@ -1245,9 +1186,10 @@ export class Device extends EventEmitter {
|
|
|
1245
1186
|
'Protocol V2 romloader mode is only supported for Pro2 and Neo.'
|
|
1246
1187
|
);
|
|
1247
1188
|
}
|
|
1248
|
-
const deviceStatusSupported =
|
|
1249
|
-
|
|
1250
|
-
|
|
1189
|
+
const deviceStatusSupported = supportsProtocolV2Message(
|
|
1190
|
+
protocolInfo,
|
|
1191
|
+
PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE
|
|
1192
|
+
);
|
|
1251
1193
|
|
|
1252
1194
|
if (runtimeMode === 'bootloader' || runtimeMode === 'romloader') {
|
|
1253
1195
|
return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
|
|
@@ -1308,7 +1250,8 @@ export class Device extends EventEmitter {
|
|
|
1308
1250
|
});
|
|
1309
1251
|
}
|
|
1310
1252
|
|
|
1311
|
-
|
|
1253
|
+
markTransportDisconnected() {
|
|
1254
|
+
this.deviceAcquired = false;
|
|
1312
1255
|
if (!this.isProtocolV2()) return;
|
|
1313
1256
|
this.protocolV2StateNeedsReload = true;
|
|
1314
1257
|
this.protocolV2RuntimeContext = undefined;
|
|
@@ -1317,11 +1260,6 @@ export class Device extends EventEmitter {
|
|
|
1317
1260
|
this.clearPreInitialized();
|
|
1318
1261
|
}
|
|
1319
1262
|
|
|
1320
|
-
markTransportDisconnected() {
|
|
1321
|
-
this.deviceAcquired = false;
|
|
1322
|
-
this.invalidateProtocolV2RuntimeState();
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
1263
|
invalidateAfterWipe() {
|
|
1326
1264
|
const deviceId = this.getCurrentDeviceId();
|
|
1327
1265
|
if (deviceId) {
|
|
@@ -1331,7 +1269,10 @@ export class Device extends EventEmitter {
|
|
|
1331
1269
|
if (this.originalDescriptor.path !== deviceId) {
|
|
1332
1270
|
deviceWalletSessionStore.deleteDevice(this.originalDescriptor.path);
|
|
1333
1271
|
}
|
|
1334
|
-
this.
|
|
1272
|
+
this.protocolV2StateNeedsReload = true;
|
|
1273
|
+
this.protocolV2RuntimeContext = undefined;
|
|
1274
|
+
this.protocolV2RuntimeContextPromise = undefined;
|
|
1275
|
+
this.protocolV2RuntimeContextRequestToken = undefined;
|
|
1335
1276
|
}
|
|
1336
1277
|
|
|
1337
1278
|
this.passphraseState = undefined;
|
|
@@ -1343,7 +1284,11 @@ export class Device extends EventEmitter {
|
|
|
1343
1284
|
markProtocolV2Reboot(rebootType: DeviceRebootType) {
|
|
1344
1285
|
if (!this.isProtocolV2()) return;
|
|
1345
1286
|
|
|
1346
|
-
this.
|
|
1287
|
+
this.protocolV2StateNeedsReload = true;
|
|
1288
|
+
this.protocolV2RuntimeContext = undefined;
|
|
1289
|
+
this.protocolV2RuntimeContextPromise = undefined;
|
|
1290
|
+
this.protocolV2RuntimeContextRequestToken = undefined;
|
|
1291
|
+
this.clearPreInitialized();
|
|
1347
1292
|
let loaderMode: 'bootloader' | 'romloader' | undefined;
|
|
1348
1293
|
if (rebootType === DeviceRebootType.Bootloader) {
|
|
1349
1294
|
loaderMode = 'bootloader';
|
|
@@ -1417,10 +1362,6 @@ export class Device extends EventEmitter {
|
|
|
1417
1362
|
if (device.features) {
|
|
1418
1363
|
this._updateFeatures(device.features);
|
|
1419
1364
|
}
|
|
1420
|
-
// Adopting another Device instance's command channel also crosses an active
|
|
1421
|
-
// link boundary. Renegotiate runtime metadata on that channel instead of
|
|
1422
|
-
// retaining ProtocolInfo from the previous instance/session.
|
|
1423
|
-
this.invalidateProtocolV2RuntimeState();
|
|
1424
1365
|
}
|
|
1425
1366
|
|
|
1426
1367
|
async run(fn?: () => Promise<void>, options?: RunOptions) {
|
package/src/device/DevicePool.ts
CHANGED
|
@@ -182,7 +182,7 @@ export class DevicePool extends EventEmitter {
|
|
|
182
182
|
try {
|
|
183
183
|
await device.initialize(initOptions);
|
|
184
184
|
if (initOptions?.refreshRuntimeState && device.isProtocolV2()) {
|
|
185
|
-
await this._refreshProtocolV2DiscoveryState(device
|
|
185
|
+
await this._refreshProtocolV2DiscoveryState(device);
|
|
186
186
|
}
|
|
187
187
|
} finally {
|
|
188
188
|
await device.release();
|
|
@@ -197,7 +197,7 @@ export class DevicePool extends EventEmitter {
|
|
|
197
197
|
await device.run(
|
|
198
198
|
async () => {
|
|
199
199
|
try {
|
|
200
|
-
await this._refreshProtocolV2DiscoveryState(device
|
|
200
|
+
await this._refreshProtocolV2DiscoveryState(device);
|
|
201
201
|
} catch (error) {
|
|
202
202
|
// Device.run releases after the callback; then propagate the actual read error.
|
|
203
203
|
refreshError = error;
|
|
@@ -206,9 +206,6 @@ export class DevicePool extends EventEmitter {
|
|
|
206
206
|
{
|
|
207
207
|
connectProtocol: initOptions.connectProtocol,
|
|
208
208
|
forceProtocolDetection: initOptions.forceProtocolDetection,
|
|
209
|
-
...(initOptions.allowLegacyProtocolV2ProtocolInfo
|
|
210
|
-
? { allowLegacyProtocolV2ProtocolInfo: true }
|
|
211
|
-
: {}),
|
|
212
209
|
}
|
|
213
210
|
);
|
|
214
211
|
if (refreshError instanceof Error) throw refreshError;
|
|
@@ -220,19 +217,10 @@ export class DevicePool extends EventEmitter {
|
|
|
220
217
|
* error; settings only supplies a label, so its failure retains the existing name.
|
|
221
218
|
* Device.getDeviceState skips unsupported status/settings calls in loader mode.
|
|
222
219
|
*/
|
|
223
|
-
static async _refreshProtocolV2DiscoveryState(device: Device
|
|
224
|
-
|
|
225
|
-
? { allowLegacyProtocolV2ProtocolInfo: true as const }
|
|
226
|
-
: {};
|
|
227
|
-
await device.getDeviceState({
|
|
228
|
-
refreshSections: ['status'],
|
|
229
|
-
...stateReadOptions,
|
|
230
|
-
});
|
|
220
|
+
static async _refreshProtocolV2DiscoveryState(device: Device) {
|
|
221
|
+
await device.getDeviceState({ refreshSections: ['status'] });
|
|
231
222
|
try {
|
|
232
|
-
await device.getDeviceState({
|
|
233
|
-
refreshSections: ['settings'],
|
|
234
|
-
...stateReadOptions,
|
|
235
|
-
});
|
|
223
|
+
await device.getDeviceState({ refreshSections: ['settings'] });
|
|
236
224
|
} catch (error) {
|
|
237
225
|
Log.debug('Unable to refresh Protocol V2 device label during discovery', error);
|
|
238
226
|
}
|
|
@@ -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>
|
|
@@ -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';
|
|
@@ -11,8 +11,6 @@ export type GetDeviceStateParams = CommonParams & {
|
|
|
11
11
|
export type DeviceStateReadOptions = {
|
|
12
12
|
refreshSections?: DeviceStateSection[];
|
|
13
13
|
includeRaw?: boolean;
|
|
14
|
-
/** Internal recovery-only compatibility used by discovery and firmware update flows. */
|
|
15
|
-
allowLegacyProtocolV2ProtocolInfo?: boolean;
|
|
16
14
|
};
|
|
17
15
|
|
|
18
16
|
export declare function getDeviceState(
|
|
@@ -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;
|
package/src/utils/pro2Nft.ts
CHANGED
|
@@ -109,34 +109,6 @@ 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
|
-
}
|
|
140
112
|
const titleLength = typeof title === 'string' ? utf8Length(title) : 0;
|
|
141
113
|
const subtitleLength =
|
|
142
114
|
typeof subtitle === 'string' ? utf8Length(subtitle) : Number.POSITIVE_INFINITY;
|
|
@@ -150,16 +122,21 @@ export function buildPro2NftBundleFromEncodedImages(options: {
|
|
|
150
122
|
throw invalidParameter('Parameter [timestampMs] must be a positive safe integer.');
|
|
151
123
|
}
|
|
152
124
|
|
|
125
|
+
const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
|
|
126
|
+
const encodedThumbnail = encodePro2Image({
|
|
127
|
+
...thumbnail,
|
|
128
|
+
alphaMode: 'black-background',
|
|
129
|
+
}).data;
|
|
153
130
|
const metadata = new TextEncoder().encode(JSON.stringify({ title, subtitle }));
|
|
154
131
|
if (metadata.byteLength === 0 || metadata.byteLength > 512) {
|
|
155
132
|
throw invalidParameter('Pro2 NFT metadata must contain 1 to 512 UTF-8 bytes.');
|
|
156
133
|
}
|
|
157
134
|
|
|
158
|
-
const hash8 = bytesToHex(blake2s(
|
|
135
|
+
const hash8 = bytesToHex(blake2s(encodedImage)).slice(0, 8);
|
|
159
136
|
return {
|
|
160
137
|
basename: `nft-${hash8}-${timestampMs}`,
|
|
161
|
-
image,
|
|
162
|
-
thumbnail,
|
|
138
|
+
image: encodedImage,
|
|
139
|
+
thumbnail: encodedThumbnail,
|
|
163
140
|
metadata,
|
|
164
141
|
};
|
|
165
142
|
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { Buffer } from 'buffer';
|
|
2
|
-
|
|
3
|
-
import { encode as encodeJpeg } from 'jpeg-js';
|
|
4
|
-
|
|
5
|
-
import { decodeCanonicalBase64, decodeJpegBase64ToRgba } from '../src/api/helpers/base64Data';
|
|
6
|
-
|
|
7
|
-
const createJpegBase64 = (width: number, height: number) => {
|
|
8
|
-
const rgba = new Uint8Array(width * height * 4).fill(0xff);
|
|
9
|
-
return encodeJpeg({ width, height, data: rgba }, 80).data.toString('base64');
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
describe('Base64 resource data', () => {
|
|
13
|
-
test('decodes canonical Base64 to a detached Uint8Array', () => {
|
|
14
|
-
const decoded = decodeCanonicalBase64({
|
|
15
|
-
value: 'AQID',
|
|
16
|
-
parameterName: 'packageBase64',
|
|
17
|
-
maxBytes: 3,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
expect(decoded).toBeInstanceOf(Uint8Array);
|
|
21
|
-
expect(Array.from(decoded)).toEqual([1, 2, 3]);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test.each(['', 'not-base64', 'AB==', 'data:image/jpeg;base64,AQID'])(
|
|
25
|
-
'rejects non-canonical Base64 input %p',
|
|
26
|
-
value => {
|
|
27
|
-
expect(() =>
|
|
28
|
-
decodeCanonicalBase64({ value, parameterName: 'data', maxBytes: 1024 })
|
|
29
|
-
).toThrow();
|
|
30
|
-
}
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
test('rejects oversized Base64 before decoding', () => {
|
|
34
|
-
const value = Buffer.alloc(64 * 1024 + 17).toString('base64');
|
|
35
|
-
expect(() =>
|
|
36
|
-
decodeCanonicalBase64({ value, parameterName: 'data', maxBytes: 64 * 1024 })
|
|
37
|
-
).toThrow('maximum supported size');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('decodes and validates a JPEG with the expected dimensions', () => {
|
|
41
|
-
const decoded = decodeJpegBase64ToRgba({
|
|
42
|
-
jpegBase64: createJpegBase64(2, 1),
|
|
43
|
-
parameterName: 'jpegBase64',
|
|
44
|
-
expectedWidth: 2,
|
|
45
|
-
expectedHeight: 1,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
expect(decoded).toMatchObject({ width: 2, height: 1 });
|
|
49
|
-
expect(decoded.data).toHaveLength(8);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('rejects non-JPEG bytes and unexpected dimensions', () => {
|
|
53
|
-
expect(() =>
|
|
54
|
-
decodeJpegBase64ToRgba({
|
|
55
|
-
jpegBase64: 'AQID',
|
|
56
|
-
parameterName: 'jpegBase64',
|
|
57
|
-
expectedWidth: 2,
|
|
58
|
-
expectedHeight: 1,
|
|
59
|
-
})
|
|
60
|
-
).toThrow('JPEG image');
|
|
61
|
-
expect(() =>
|
|
62
|
-
decodeJpegBase64ToRgba({
|
|
63
|
-
jpegBase64: createJpegBase64(2, 1),
|
|
64
|
-
parameterName: 'jpegBase64',
|
|
65
|
-
expectedWidth: 1,
|
|
66
|
-
expectedHeight: 1,
|
|
67
|
-
})
|
|
68
|
-
).toThrow('1x1');
|
|
69
|
-
});
|
|
70
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { isLegacyProtocolV2FirmwareRecoveryMethod } from '../src/core/protocolV2LegacyRecovery';
|
|
2
|
-
|
|
3
|
-
import type { BaseMethod } from '../src/api/BaseMethod';
|
|
4
|
-
|
|
5
|
-
const createMethod = (
|
|
6
|
-
name: string,
|
|
7
|
-
payload: Record<string, unknown>
|
|
8
|
-
): Pick<BaseMethod, 'name' | 'payload'> => ({
|
|
9
|
-
name,
|
|
10
|
-
payload: { method: name, ...payload },
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('Protocol V2 legacy firmware recovery policy', () => {
|
|
14
|
-
test.each([
|
|
15
|
-
['firmware state read', 'getDeviceState', { scope: 'firmware' }],
|
|
16
|
-
['firmware release check', 'checkAllFirmwareRelease', {}],
|
|
17
|
-
['firmware update', 'firmwareUpdateV4', {}],
|
|
18
|
-
])('allows legacy ProtocolInfo for %s', (_label, name, payload) => {
|
|
19
|
-
expect(isLegacyProtocolV2FirmwareRecoveryMethod(createMethod(name, payload))).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test.each([
|
|
23
|
-
['runtime state read', 'getDeviceState', { scope: 'runtime' }],
|
|
24
|
-
['settings state read', 'getDeviceState', { scope: 'settings' }],
|
|
25
|
-
['ordinary API', 'getFeatures', {}],
|
|
26
|
-
])('rejects legacy ProtocolInfo for %s', (_label, name, payload) => {
|
|
27
|
-
expect(isLegacyProtocolV2FirmwareRecoveryMethod(createMethod(name, payload))).toBe(false);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { HardwareTopLevelSdk } from '../src';
|
|
2
|
-
import { findMethod } from '../src/api/utils';
|
|
3
|
-
|
|
4
|
-
import type { LowLevelCoreApi } from '../src';
|
|
5
|
-
|
|
6
|
-
jest.mock('../src/data/config', () => ({
|
|
7
|
-
DEFAULT_DOMAIN: 'https://example.com/',
|
|
8
|
-
getSDKVersion: () => '0.0.0-test',
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
const crossJsonOnlyBridge = (value: unknown): unknown => JSON.parse(JSON.stringify(value));
|
|
12
|
-
|
|
13
|
-
describe('Base64 resource boundary', () => {
|
|
14
|
-
test('keeps Portfolio, wallpaper and NFT payloads JSON-safe across TopLevel and LowLevel', async () => {
|
|
15
|
-
const restoredPayloads: Array<Record<string, any>> = [];
|
|
16
|
-
const lowLevelApi = {
|
|
17
|
-
call: jest.fn(params => {
|
|
18
|
-
const method = findMethod({ id: 1, payload: crossJsonOnlyBridge(params) } as any);
|
|
19
|
-
restoredPayloads.push(method.payload);
|
|
20
|
-
return Promise.resolve({ success: true, payload: {} });
|
|
21
|
-
}),
|
|
22
|
-
init: jest.fn(() => Promise.resolve(true)),
|
|
23
|
-
} as unknown as LowLevelCoreApi;
|
|
24
|
-
const sdk = HardwareTopLevelSdk();
|
|
25
|
-
await sdk.init({}, lowLevelApi);
|
|
26
|
-
|
|
27
|
-
await sdk.uploadPortfolio('connect-id', { packageBase64: 'AQID' });
|
|
28
|
-
await sdk.deviceUploadWallpaper('connect-id', { jpegBase64: '/9j/' });
|
|
29
|
-
await sdk.deviceUploadNft('connect-id', {
|
|
30
|
-
imageJpegBase64: '/9j/',
|
|
31
|
-
thumbnailJpegBase64: '/9j/',
|
|
32
|
-
title: 'NFT',
|
|
33
|
-
subtitle: '',
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
expect(restoredPayloads.map(payload => payload.method)).toEqual([
|
|
37
|
-
'uploadPortfolio',
|
|
38
|
-
'deviceUploadWallpaper',
|
|
39
|
-
'deviceUploadNft',
|
|
40
|
-
]);
|
|
41
|
-
expect(restoredPayloads[0].packageBase64).toBe('AQID');
|
|
42
|
-
expect(restoredPayloads[1].jpegBase64).toBe('/9j/');
|
|
43
|
-
expect(restoredPayloads[2]).toMatchObject({
|
|
44
|
-
imageJpegBase64: '/9j/',
|
|
45
|
-
thumbnailJpegBase64: '/9j/',
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
2
|
-
|
|
3
|
-
import TonSignMessage from '../src/api/ton/TonSignMessage';
|
|
4
|
-
|
|
5
|
-
jest.mock('../src/data/config', () => ({
|
|
6
|
-
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
7
|
-
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
const createMethod = (deviceType: EDeviceType) => {
|
|
11
|
-
const method = new TonSignMessage({
|
|
12
|
-
id: 1,
|
|
13
|
-
payload: {
|
|
14
|
-
method: 'tonSignMessage',
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
Object.defineProperty(method, 'device', {
|
|
19
|
-
value: {
|
|
20
|
-
getCurrentDeviceType: () => deviceType,
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
return method;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
describe('TonSignMessage response compatibility', () => {
|
|
28
|
-
test.each([EDeviceType.Pro2, EDeviceType.Neo])(
|
|
29
|
-
'uses the corrected signing_message field for %s',
|
|
30
|
-
async deviceType => {
|
|
31
|
-
const method = createMethod(deviceType);
|
|
32
|
-
|
|
33
|
-
await expect(
|
|
34
|
-
method.processTxRequest(
|
|
35
|
-
{
|
|
36
|
-
signature: 'signature',
|
|
37
|
-
signing_message: 'serialized-message',
|
|
38
|
-
},
|
|
39
|
-
''
|
|
40
|
-
)
|
|
41
|
-
).resolves.toEqual({
|
|
42
|
-
signature: 'signature',
|
|
43
|
-
signing_message: 'serialized-message',
|
|
44
|
-
skip_validate: false,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
test('normalizes the legacy Protocol V1 field without removing it', async () => {
|
|
50
|
-
const method = createMethod(EDeviceType.Classic1s);
|
|
51
|
-
|
|
52
|
-
await expect(
|
|
53
|
-
method.processTxRequest(
|
|
54
|
-
{
|
|
55
|
-
signature: 'signature',
|
|
56
|
-
signning_message: 'message-hash',
|
|
57
|
-
},
|
|
58
|
-
''
|
|
59
|
-
)
|
|
60
|
-
).resolves.toEqual({
|
|
61
|
-
signature: 'signature',
|
|
62
|
-
signning_message: 'message-hash',
|
|
63
|
-
signing_message: 'message-hash',
|
|
64
|
-
skip_validate: true,
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('skips validation when blind signing omits the signing message', async () => {
|
|
69
|
-
const method = createMethod(EDeviceType.Pro2);
|
|
70
|
-
|
|
71
|
-
await expect(
|
|
72
|
-
method.processTxRequest(
|
|
73
|
-
{
|
|
74
|
-
signature: 'signature',
|
|
75
|
-
},
|
|
76
|
-
''
|
|
77
|
-
)
|
|
78
|
-
).resolves.toEqual({
|
|
79
|
-
signature: 'signature',
|
|
80
|
-
signing_message: undefined,
|
|
81
|
-
skip_validate: true,
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export declare function decodeCanonicalBase64({ value, parameterName, maxBytes, }: {
|
|
2
|
-
value: unknown;
|
|
3
|
-
parameterName: string;
|
|
4
|
-
maxBytes: number;
|
|
5
|
-
}): Uint8Array;
|
|
6
|
-
export declare function decodeJpegBase64ToRgba({ jpegBase64, parameterName, expectedWidth, expectedHeight, }: {
|
|
7
|
-
jpegBase64: unknown;
|
|
8
|
-
parameterName: string;
|
|
9
|
-
expectedWidth: number;
|
|
10
|
-
expectedHeight: number;
|
|
11
|
-
}): {
|
|
12
|
-
width: number;
|
|
13
|
-
height: number;
|
|
14
|
-
data: Uint8Array;
|
|
15
|
-
};
|
|
16
|
-
//# sourceMappingURL=base64Data.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"base64Data.d.ts","sourceRoot":"","sources":["../../../src/api/helpers/base64Data.ts"],"names":[],"mappings":"AAUA,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,aAAa,EACb,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,UAAU,CAmBb;AAED,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,aAAa,EACb,aAAa,EACb,cAAc,GACf,EAAE;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAmCtD"}
|