@onekeyfe/hd-core 1.2.0-alpha.41 → 1.2.0-alpha.43
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__/AllNetworkGetAddressBase.tracing.test.ts +78 -0
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +14 -1
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/deviceUploadNft.test.ts +272 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
- package/__tests__/get-device-state.test.ts +132 -12
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/open-wallet-session.test.ts +366 -31
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-v2-resources.test.ts +204 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +196 -35
- package/__tests__/protocol-v2.test.ts +568 -957
- package/__tests__/protocolV2UiInteraction.test.ts +63 -0
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +0 -11
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +3 -7
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +6 -25
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -8
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -7
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +0 -2
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +1 -9
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/core/deviceEventRegistration.d.ts +2 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +3 -2
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +24 -4
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +28 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +115 -184
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2051 -3349
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +30 -0
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +4 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -4
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +1 -4
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +0 -69
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +3 -10
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/settings.d.ts +12 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.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 +31 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +8 -10
- package/src/api/CheckAllFirmwareRelease.ts +40 -53
- package/src/api/FirmwareUpdateV2.ts +120 -298
- package/src/api/FirmwareUpdateV3.ts +57 -263
- package/src/api/FirmwareUpdateV4.ts +307 -912
- package/src/api/GetPassphraseState.ts +7 -1
- package/src/api/OpenWalletSession.ts +54 -17
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +32 -11
- package/src/api/device/DeviceUnlock.ts +1 -1
- package/src/api/device/DeviceUpdateBootloader.ts +6 -122
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -57
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +22 -140
- package/src/api/helpers/protocolV2FileWrite.ts +11 -4
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +136 -77
- package/src/data/messages/messages-protocol-v2.json +68 -0
- package/src/data-manager/DataManager.ts +63 -38
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +203 -21
- package/src/events/device.ts +4 -0
- package/src/events/logBlockEvent.ts +1 -0
- package/src/events/ui-request.ts +33 -2
- package/src/index.ts +0 -5
- package/src/inject.ts +3 -22
- package/src/lowLevelInject.ts +1 -5
- package/src/protocols/protocol-v2/features.ts +9 -2
- package/src/protocols/protocol-v2/resources.ts +233 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +124 -0
- package/src/protocols/protocol-v2/walletSession.ts +154 -30
- package/src/topLevelInject.ts +1 -5
- package/src/types/api/checkAllFirmwareRelease.ts +1 -4
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +0 -18
- package/src/types/api/firmwareUpdate.ts +0 -76
- package/src/types/api/index.ts +2 -14
- package/src/types/api/protocolV2.ts +13 -0
- package/src/types/settings.ts +26 -13
- package/src/utils/deviceFeaturesUtils.ts +4 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +142 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
- package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
- package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
- package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -30
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/dist/api/firmware/progressThrottle.d.ts +0 -3
- package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
- package/src/api/firmware/FirmwareHostBinding.ts +0 -100
- package/src/api/firmware/FirmwarePreparationError.ts +0 -12
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
- package/src/api/firmware/FirmwareUpdatePlan.ts +0 -745
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
- package/src/api/firmware/progressThrottle.ts +0 -44
- package/src/protocols/protocol-v2/lockedError.ts +0 -10
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
- package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -38
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -10,7 +10,6 @@ import { bytesToHex } from '../helpers/hexUtils';
|
|
|
10
10
|
import { DataManager } from '../../data-manager';
|
|
11
11
|
import { DevicePool } from '../../device/DevicePool';
|
|
12
12
|
import { buildProtocolV1FeaturesPayload } from '../../deviceProfile';
|
|
13
|
-
import { openFirmwareByteSource, readFirmwareByteSourceFully } from './FirmwareArtifactSource';
|
|
14
13
|
|
|
15
14
|
import type { KnownDevice } from '../../types';
|
|
16
15
|
import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
|
|
@@ -18,7 +17,6 @@ import type { PROTO } from '../../constants';
|
|
|
18
17
|
import type { CoreMessage, IFirmwareUpdateProgressType } from '../../events';
|
|
19
18
|
import type { Success } from '@onekeyfe/hd-transport';
|
|
20
19
|
import type { Device } from '../../device/Device';
|
|
21
|
-
import type { FirmwareByteSource } from './FirmwareArtifactSource';
|
|
22
20
|
|
|
23
21
|
const NEW_BOOT_UPRATE_FIRMWARE_VERSION = '2.4.5';
|
|
24
22
|
const SESSION_ERROR = 'session not found';
|
|
@@ -90,20 +88,17 @@ export const waitBleInstall = async (updateType: string) => {
|
|
|
90
88
|
}
|
|
91
89
|
};
|
|
92
90
|
|
|
93
|
-
const
|
|
94
|
-
if (payload instanceof ArrayBuffer) {
|
|
95
|
-
return payload;
|
|
96
|
-
}
|
|
97
|
-
return new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength).slice().buffer;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export const uploadFirmwareFromSource = async (
|
|
91
|
+
export const uploadFirmware = async (
|
|
101
92
|
updateType: 'firmware' | 'ble',
|
|
102
93
|
typedCall: TypedCall,
|
|
103
94
|
postMessage: (message: CoreMessage) => void,
|
|
104
95
|
device: Device,
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
{
|
|
97
|
+
payload,
|
|
98
|
+
rebootOnSuccess,
|
|
99
|
+
}: PROTO.FirmwareUpload & {
|
|
100
|
+
rebootOnSuccess?: boolean;
|
|
101
|
+
},
|
|
107
102
|
isUpdateBootloader?: boolean
|
|
108
103
|
) => {
|
|
109
104
|
const deviceType = device.getCurrentDeviceType();
|
|
@@ -124,20 +119,20 @@ export const uploadFirmwareFromSource = async (
|
|
|
124
119
|
if (supportUpgradeFileHeader) {
|
|
125
120
|
// Extract and validate firmware header (first 1KB)
|
|
126
121
|
const HEADER_SIZE = 1024;
|
|
127
|
-
if (
|
|
122
|
+
if (payload.byteLength < HEADER_SIZE) {
|
|
128
123
|
throw ERRORS.TypedError(
|
|
129
124
|
HardwareErrorCode.RuntimeError,
|
|
130
|
-
`firmware payload too small: ${
|
|
125
|
+
`firmware payload too small: ${payload.byteLength} bytes, expected at least ${HEADER_SIZE} bytes`
|
|
131
126
|
);
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
Log.debug('Uploading firmware header:', {
|
|
135
130
|
size: HEADER_SIZE,
|
|
136
|
-
totalSize:
|
|
131
|
+
totalSize: payload.byteLength,
|
|
137
132
|
});
|
|
138
133
|
postProgressTip(device, 'UploadingFirmwareHeader', postMessage);
|
|
139
134
|
|
|
140
|
-
const header = new Uint8Array(
|
|
135
|
+
const header = new Uint8Array(payload.slice(0, HEADER_SIZE));
|
|
141
136
|
|
|
142
137
|
try {
|
|
143
138
|
const headerRes = await typedCall('UpgradeFileHeader', 'Success', {
|
|
@@ -174,9 +169,6 @@ export const uploadFirmwareFromSource = async (
|
|
|
174
169
|
postProgressMessage(device, 0, 'installingFirmware', postMessage);
|
|
175
170
|
let updateResponse: TypedResponseMessage<'Success'>;
|
|
176
171
|
try {
|
|
177
|
-
// Classic/Mini bootloaders accept one protobuf payload and do not expose
|
|
178
|
-
// a request-range protocol. Other device families remain fully streamed.
|
|
179
|
-
const payload = await readFirmwareByteSourceFully(source);
|
|
180
172
|
updateResponse = await typedCall('FirmwareUpload', 'Success', {
|
|
181
173
|
payload,
|
|
182
174
|
});
|
|
@@ -208,7 +200,9 @@ export const uploadFirmwareFromSource = async (
|
|
|
208
200
|
updateType,
|
|
209
201
|
postMessage,
|
|
210
202
|
device,
|
|
211
|
-
|
|
203
|
+
{
|
|
204
|
+
payload,
|
|
205
|
+
},
|
|
212
206
|
rebootOnSuccess
|
|
213
207
|
);
|
|
214
208
|
return response.message;
|
|
@@ -217,7 +211,7 @@ export const uploadFirmwareFromSource = async (
|
|
|
217
211
|
|
|
218
212
|
postConfirmationMessage(device);
|
|
219
213
|
postProgressTip(device, 'ConfirmOnDevice', postMessage);
|
|
220
|
-
const length =
|
|
214
|
+
const length = payload.byteLength;
|
|
221
215
|
|
|
222
216
|
let response = await typedCall('FirmwareErase', ['FirmwareRequest', 'Success'], { length });
|
|
223
217
|
postProgressTip(device, 'FirmwareEraseSuccess', postMessage);
|
|
@@ -226,7 +220,7 @@ export const uploadFirmwareFromSource = async (
|
|
|
226
220
|
const start = response.message.offset!;
|
|
227
221
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
228
222
|
const end = response.message.offset! + response.message.length!;
|
|
229
|
-
const chunk =
|
|
223
|
+
const chunk = payload.slice(start, end);
|
|
230
224
|
|
|
231
225
|
if (start > 0) {
|
|
232
226
|
postProgressMessage(
|
|
@@ -255,45 +249,11 @@ export const uploadFirmwareFromSource = async (
|
|
|
255
249
|
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'uploadFirmware: unknown device model');
|
|
256
250
|
};
|
|
257
251
|
|
|
258
|
-
export const uploadFirmware = async (
|
|
259
|
-
updateType: 'firmware' | 'ble',
|
|
260
|
-
typedCall: TypedCall,
|
|
261
|
-
postMessage: (message: CoreMessage) => void,
|
|
262
|
-
device: Device,
|
|
263
|
-
{
|
|
264
|
-
payload,
|
|
265
|
-
rebootOnSuccess,
|
|
266
|
-
}: PROTO.FirmwareUpload & {
|
|
267
|
-
rebootOnSuccess?: boolean;
|
|
268
|
-
},
|
|
269
|
-
isUpdateBootloader?: boolean
|
|
270
|
-
) => {
|
|
271
|
-
const source = await openFirmwareByteSource({
|
|
272
|
-
binary: toArrayBuffer(payload),
|
|
273
|
-
});
|
|
274
|
-
if (!source) {
|
|
275
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'firmware payload is empty');
|
|
276
|
-
}
|
|
277
|
-
try {
|
|
278
|
-
return await uploadFirmwareFromSource(
|
|
279
|
-
updateType,
|
|
280
|
-
typedCall,
|
|
281
|
-
postMessage,
|
|
282
|
-
device,
|
|
283
|
-
source,
|
|
284
|
-
rebootOnSuccess,
|
|
285
|
-
isUpdateBootloader
|
|
286
|
-
);
|
|
287
|
-
} finally {
|
|
288
|
-
await source.close();
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
|
|
292
252
|
const newTouchUpdateProcess = async (
|
|
293
253
|
updateType: 'firmware' | 'ble',
|
|
294
254
|
postMessage: (message: CoreMessage) => void,
|
|
295
255
|
device: Device,
|
|
296
|
-
|
|
256
|
+
{ payload }: PROTO.FirmwareUpload,
|
|
297
257
|
rebootOnSuccess = true
|
|
298
258
|
) => {
|
|
299
259
|
let typedCall = device.getCommands().typedCall.bind(device.getCommands());
|
|
@@ -303,13 +263,13 @@ const newTouchUpdateProcess = async (
|
|
|
303
263
|
const env = DataManager.getSettings('env');
|
|
304
264
|
const perPackageSize = DataManager.isBleConnect(env) ? 16 : 128;
|
|
305
265
|
const chunkSize = 1024 * perPackageSize;
|
|
306
|
-
const totalChunks = Math.ceil(
|
|
266
|
+
const totalChunks = Math.ceil(payload.byteLength / chunkSize);
|
|
307
267
|
let offset = 0;
|
|
308
268
|
for (let i = 0; i < totalChunks; i++) {
|
|
309
269
|
const chunkStart = i * chunkSize;
|
|
310
|
-
const chunkEnd = Math.min(chunkStart + chunkSize,
|
|
270
|
+
const chunkEnd = Math.min(chunkStart + chunkSize, payload.byteLength);
|
|
311
271
|
const chunkLength = chunkEnd - chunkStart;
|
|
312
|
-
const chunk =
|
|
272
|
+
const chunk = payload.slice(chunkStart, chunkEnd);
|
|
313
273
|
const overwrite = i === 0;
|
|
314
274
|
const progress = Math.round(((i + 1) / totalChunks) * 100);
|
|
315
275
|
const writeRes = await emmcFileWriteWithRetry(
|
|
@@ -530,67 +490,6 @@ export const updateResource = async (
|
|
|
530
490
|
return processResourceRequest(typedCall, res, data);
|
|
531
491
|
};
|
|
532
492
|
|
|
533
|
-
export const updateResourceFromSource = async (
|
|
534
|
-
typedCall: TypedCall,
|
|
535
|
-
fileName: string,
|
|
536
|
-
source: FirmwareByteSource,
|
|
537
|
-
onConfirmAfter?: () => void
|
|
538
|
-
) => {
|
|
539
|
-
const initialLength = Math.min(INIT_DATA_CHUNK_SIZE, source.size);
|
|
540
|
-
const initialChunk = new Uint8Array(await source.readAt(0, initialLength));
|
|
541
|
-
let response = await typedCall('ResourceUpdate', ['ResourceRequest', 'Success'], {
|
|
542
|
-
file_name: fileName,
|
|
543
|
-
data_length: source.size,
|
|
544
|
-
initial_data_chunk: bytesToHex(initialChunk),
|
|
545
|
-
hash: bytesToHex(blake2s(initialChunk)),
|
|
546
|
-
});
|
|
547
|
-
onConfirmAfter?.();
|
|
548
|
-
|
|
549
|
-
while (response.type !== 'Success') {
|
|
550
|
-
const { offset, data_length: dataLength } = response.message;
|
|
551
|
-
if (
|
|
552
|
-
!Number.isSafeInteger(offset) ||
|
|
553
|
-
offset === undefined ||
|
|
554
|
-
offset < 0 ||
|
|
555
|
-
!Number.isSafeInteger(dataLength) ||
|
|
556
|
-
dataLength === undefined ||
|
|
557
|
-
dataLength <= 0 ||
|
|
558
|
-
offset + dataLength > source.size
|
|
559
|
-
) {
|
|
560
|
-
throw ERRORS.TypedError(
|
|
561
|
-
HardwareErrorCode.RuntimeError,
|
|
562
|
-
'Device requested an invalid firmware resource range'
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
const chunk = new Uint8Array(await source.readAt(offset, dataLength));
|
|
566
|
-
response = await typedCall('ResourceAck', ['ResourceRequest', 'Success'], {
|
|
567
|
-
data_chunk: bytesToHex(chunk),
|
|
568
|
-
hash: bytesToHex(blake2s(chunk)),
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
return response.message;
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
export const updateResourcesFromSources = async (
|
|
575
|
-
typedCall: TypedCall,
|
|
576
|
-
postMessage: (message: CoreMessage) => void,
|
|
577
|
-
device: Device,
|
|
578
|
-
entries: ReadonlyArray<{ entryName: string; source: FirmwareByteSource }>
|
|
579
|
-
) => {
|
|
580
|
-
postProgressTip(device, 'UpdateSysResource', postMessage);
|
|
581
|
-
for (const [index, entry] of entries.entries()) {
|
|
582
|
-
await updateResourceFromSource(typedCall, entry.entryName, entry.source);
|
|
583
|
-
postProgressMessage(
|
|
584
|
-
device,
|
|
585
|
-
Math.floor(((index + 1) / entries.length) * 100),
|
|
586
|
-
'installingFirmware',
|
|
587
|
-
postMessage
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
postProgressTip(device, 'UpdateSysResourceSuccess', postMessage);
|
|
591
|
-
return true;
|
|
592
|
-
};
|
|
593
|
-
|
|
594
493
|
export const updateResources = async (
|
|
595
494
|
typedCall: TypedCall,
|
|
596
495
|
postMessage: (message: CoreMessage) => void,
|
|
@@ -625,28 +524,11 @@ export const updateBootloader = async (
|
|
|
625
524
|
typedCall: TypedCall,
|
|
626
525
|
postMessage: (message: CoreMessage) => void,
|
|
627
526
|
device: Device,
|
|
628
|
-
|
|
629
|
-
) => {
|
|
630
|
-
const source = await openFirmwareByteSource({ binary: data });
|
|
631
|
-
if (!source) {
|
|
632
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'bootloader payload is empty');
|
|
633
|
-
}
|
|
634
|
-
try {
|
|
635
|
-
return await updateBootloaderFromSource(typedCall, postMessage, device, source);
|
|
636
|
-
} finally {
|
|
637
|
-
await source.close();
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
|
|
641
|
-
export const updateBootloaderFromSource = async (
|
|
642
|
-
typedCall: TypedCall,
|
|
643
|
-
postMessage: (message: CoreMessage) => void,
|
|
644
|
-
device: Device,
|
|
645
|
-
source: FirmwareByteSource
|
|
527
|
+
source: ArrayBuffer
|
|
646
528
|
) => {
|
|
647
529
|
postProgressTip(device, 'UpdateBootloader', postMessage);
|
|
648
530
|
postProgressMessage(device, Math.floor(0), 'installingFirmware', postMessage);
|
|
649
|
-
await
|
|
531
|
+
await updateResource(typedCall, 'bootloader.bin', source, () => {
|
|
650
532
|
postProcessingMessage('resource', postMessage);
|
|
651
533
|
});
|
|
652
534
|
postProgressMessage(device, Math.floor(100), 'installingFirmware', postMessage);
|
|
@@ -31,19 +31,21 @@ export type ProtocolV2FileWriteOptions = {
|
|
|
31
31
|
uiPercentage?: number;
|
|
32
32
|
timeoutMs?: number;
|
|
33
33
|
maxChunkRetries?: number;
|
|
34
|
+
paceMs?: number;
|
|
34
35
|
throwIfAborted?: () => void;
|
|
35
36
|
onProgress?: (progress: ProtocolV2FileWriteProgress) => void;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
const MIN_FILE_CHUNK_SIZE = 64;
|
|
39
40
|
|
|
40
|
-
function
|
|
41
|
+
export function isProtocolV2ResponseTimeout(error: unknown) {
|
|
41
42
|
if (!error || typeof error !== 'object') return false;
|
|
42
|
-
const candidate = error as { errorCode?: number; code?: number; message?: string };
|
|
43
|
+
const candidate = error as { errorCode?: number; code?: number | string; message?: string };
|
|
43
44
|
const code = candidate.errorCode ?? candidate.code;
|
|
44
45
|
return (
|
|
45
46
|
code === HardwareErrorCode.BleTimeoutError ||
|
|
46
|
-
|
|
47
|
+
code === 'response-timeout' ||
|
|
48
|
+
/(?:BLE|Lowlevel|Protocol V2) response timeout/i.test(candidate.message ?? '')
|
|
47
49
|
);
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -147,7 +149,7 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
147
149
|
);
|
|
148
150
|
isWritePending = false;
|
|
149
151
|
} catch (error) {
|
|
150
|
-
if (retryCount >= maxChunkRetries || !
|
|
152
|
+
if (retryCount >= maxChunkRetries || !isProtocolV2ResponseTimeout(error)) throw error;
|
|
151
153
|
retryCount += 1;
|
|
152
154
|
options.throwIfAborted?.();
|
|
153
155
|
}
|
|
@@ -186,6 +188,11 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
186
188
|
elapsedMs > 0 ? Math.round((transferredBytes / elapsedMs) * 1000) : undefined,
|
|
187
189
|
elapsedMs,
|
|
188
190
|
});
|
|
191
|
+
if (options.paceMs && options.paceMs > 0) {
|
|
192
|
+
await new Promise(resolve => {
|
|
193
|
+
setTimeout(resolve, options.paceMs);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
return {
|
package/src/api/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export { default as promptWebDeviceAccess } from './PromptWebDeviceAccess';
|
|
|
49
49
|
export { default as deviceReboot } from './protocol-v2/DeviceReboot';
|
|
50
50
|
export { default as deviceGetOnboardingStatus } from './protocol-v2/DeviceGetOnboardingStatus';
|
|
51
51
|
export { default as deviceUploadWallpaper } from './protocol-v2/DeviceUploadWallpaper';
|
|
52
|
+
export { default as deviceUploadNft } from './protocol-v2/DeviceUploadNft';
|
|
52
53
|
export { default as uploadPortfolio } from './UploadPortfolio';
|
|
53
54
|
|
|
54
55
|
export { default as cipherKeyValue } from './CipherKeyValue';
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode, createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
|
|
4
|
+
import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
|
|
5
|
+
import {
|
|
6
|
+
PRO2_NFT_DEFAULT_CHUNK_SIZE,
|
|
7
|
+
PRO2_NFT_DEFAULT_PACE_MS,
|
|
8
|
+
PRO2_NFT_DEFAULT_TIMEOUT_MS,
|
|
9
|
+
PRO2_NFT_DIRECTORY,
|
|
10
|
+
PRO2_NFT_MAX_CHUNK_SIZE,
|
|
11
|
+
PRO2_NFT_MAX_ITEMS,
|
|
12
|
+
PRO2_NFT_MIN_CHUNK_SIZE,
|
|
13
|
+
type Pro2NftBundle,
|
|
14
|
+
type Pro2NftImage,
|
|
15
|
+
buildPro2NftBundle,
|
|
16
|
+
getCompletePro2NftBasenames,
|
|
17
|
+
} from '../../utils/pro2Nft';
|
|
18
|
+
import { BaseMethod } from '../BaseMethod';
|
|
19
|
+
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
20
|
+
import { isProtocolV2ResponseTimeout, writeProtocolV2File } from '../helpers/protocolV2FileWrite';
|
|
21
|
+
|
|
22
|
+
export type DeviceUploadNftParams = {
|
|
23
|
+
image: Pro2NftImage;
|
|
24
|
+
thumbnail: Pro2NftImage;
|
|
25
|
+
title: string;
|
|
26
|
+
subtitle: string;
|
|
27
|
+
timestampMs?: number;
|
|
28
|
+
chunkSize?: number;
|
|
29
|
+
paceMs?: number;
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type DeviceUploadNftResponse = {
|
|
34
|
+
basename: string;
|
|
35
|
+
imagePath: string;
|
|
36
|
+
thumbnailPath: string;
|
|
37
|
+
metadataPath: string;
|
|
38
|
+
totalSize: number;
|
|
39
|
+
nftUpdated: true;
|
|
40
|
+
message?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
44
|
+
const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
|
|
45
|
+
const NFT_UPDATE_MESSAGE_TYPE = 61500;
|
|
46
|
+
|
|
47
|
+
export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
48
|
+
private bundle?: Pro2NftBundle;
|
|
49
|
+
|
|
50
|
+
getSupportedProtocols() {
|
|
51
|
+
return ['V2'] as const;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
init() {
|
|
55
|
+
const {
|
|
56
|
+
image,
|
|
57
|
+
thumbnail,
|
|
58
|
+
title,
|
|
59
|
+
subtitle,
|
|
60
|
+
timestampMs = Date.now(),
|
|
61
|
+
chunkSize = PRO2_NFT_DEFAULT_CHUNK_SIZE,
|
|
62
|
+
paceMs = PRO2_NFT_DEFAULT_PACE_MS,
|
|
63
|
+
timeoutMs = PRO2_NFT_DEFAULT_TIMEOUT_MS,
|
|
64
|
+
} = this.payload;
|
|
65
|
+
if (
|
|
66
|
+
!Number.isInteger(chunkSize) ||
|
|
67
|
+
chunkSize < PRO2_NFT_MIN_CHUNK_SIZE ||
|
|
68
|
+
chunkSize > PRO2_NFT_MAX_CHUNK_SIZE
|
|
69
|
+
) {
|
|
70
|
+
throw invalidParameter(
|
|
71
|
+
`Parameter [chunkSize] must be an integer between ${PRO2_NFT_MIN_CHUNK_SIZE} and ${PRO2_NFT_MAX_CHUNK_SIZE}.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!Number.isInteger(paceMs) || paceMs < 0) {
|
|
75
|
+
throw invalidParameter('Parameter [paceMs] must be a non-negative integer.');
|
|
76
|
+
}
|
|
77
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
|
|
78
|
+
throw invalidParameter('Parameter [timeoutMs] must be a positive integer.');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.bundle = buildPro2NftBundle({ image, thumbnail, title, subtitle, timestampMs });
|
|
82
|
+
this.params = { image, thumbnail, title, subtitle, timestampMs, chunkSize, paceMs, timeoutMs };
|
|
83
|
+
this.unlockPolicy = 'none';
|
|
84
|
+
this.skipForceUpdateCheck = true;
|
|
85
|
+
this.useDevicePassphraseState = false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private async assertCapabilities() {
|
|
89
|
+
const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
|
|
90
|
+
const hasFileWrite = supportsProtocolV2Message(
|
|
91
|
+
protocolInfo,
|
|
92
|
+
FILESYSTEM_FILE_WRITE_MESSAGE_TYPE
|
|
93
|
+
);
|
|
94
|
+
const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
|
|
95
|
+
const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
|
|
96
|
+
if (!hasFileWrite || !hasDirList || !hasNftUpdate) {
|
|
97
|
+
throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private async assertStorageCapacity(basename: string) {
|
|
102
|
+
const { message } = await this.device.commands.typedCall(
|
|
103
|
+
'FilesystemDirList',
|
|
104
|
+
'FilesystemDir',
|
|
105
|
+
{ path: PRO2_NFT_DIRECTORY, depth: 1 },
|
|
106
|
+
{ timeoutMs: this.params.timeoutMs }
|
|
107
|
+
);
|
|
108
|
+
const existingBasenames = getCompletePro2NftBasenames(message.child_files);
|
|
109
|
+
if (existingBasenames.size >= PRO2_NFT_MAX_ITEMS && !existingBasenames.has(basename)) {
|
|
110
|
+
throw ERRORS.TypedError(HardwareErrorCode.NftStorageLimitReached, undefined, {
|
|
111
|
+
count: existingBasenames.size,
|
|
112
|
+
limit: PRO2_NFT_MAX_ITEMS,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private async updateNft(basename: string) {
|
|
118
|
+
const params = { file_name_no_ext: basename };
|
|
119
|
+
const options = { timeoutMs: this.params.timeoutMs };
|
|
120
|
+
try {
|
|
121
|
+
return await this.device.commands.typedCall('NftUpdate', 'Success', params, options);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (!isProtocolV2ResponseTimeout(error)) {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
this.throwIfAborted();
|
|
127
|
+
return this.device.commands.typedCall('NftUpdate', 'Success', params, options);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async run(): Promise<DeviceUploadNftResponse> {
|
|
132
|
+
const { bundle } = this;
|
|
133
|
+
if (!bundle) throw invalidParameter('NFT data has not been initialized.');
|
|
134
|
+
|
|
135
|
+
await this.assertCapabilities();
|
|
136
|
+
this.throwIfAborted();
|
|
137
|
+
await this.assertStorageCapacity(bundle.basename);
|
|
138
|
+
this.throwIfAborted();
|
|
139
|
+
|
|
140
|
+
const files = [
|
|
141
|
+
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
|
|
142
|
+
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
|
|
143
|
+
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
|
|
144
|
+
];
|
|
145
|
+
const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
|
|
146
|
+
let transferredBeforeFile = 0;
|
|
147
|
+
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
const transferredAtFileStart = transferredBeforeFile;
|
|
150
|
+
await writeProtocolV2File({
|
|
151
|
+
commands: this.device.commands,
|
|
152
|
+
path: file.path,
|
|
153
|
+
data: file.data,
|
|
154
|
+
totalSize: file.data.byteLength,
|
|
155
|
+
chunkSize: this.params.chunkSize,
|
|
156
|
+
timeoutMs: this.params.timeoutMs,
|
|
157
|
+
paceMs: this.params.paceMs,
|
|
158
|
+
overwrite: true,
|
|
159
|
+
append: false,
|
|
160
|
+
throwIfAborted: () => this.throwIfAborted(),
|
|
161
|
+
onProgress: progress => {
|
|
162
|
+
if (typeof this.postMessage !== 'function') return;
|
|
163
|
+
const transferredBytes = transferredAtFileStart + progress.transferredBytes;
|
|
164
|
+
this.postMessage(
|
|
165
|
+
createUiMessage(UI_REQUEST.DEVICE_PROGRESS, {
|
|
166
|
+
...progress,
|
|
167
|
+
progress: Math.floor((transferredBytes / totalSize) * 100),
|
|
168
|
+
transferredBytes,
|
|
169
|
+
totalBytes: totalSize,
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
transferredBeforeFile += file.data.byteLength;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.throwIfAborted();
|
|
178
|
+
const response = await this.updateNft(bundle.basename);
|
|
179
|
+
return {
|
|
180
|
+
basename: bundle.basename,
|
|
181
|
+
imagePath: files[0].path,
|
|
182
|
+
thumbnailPath: files[1].path,
|
|
183
|
+
metadataPath: files[2].path,
|
|
184
|
+
totalSize,
|
|
185
|
+
nftUpdated: true,
|
|
186
|
+
message: response.message?.message,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -13,7 +13,9 @@ export default class ProtocolInfoRequest extends BaseMethod {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async run() {
|
|
16
|
-
const res = await this.device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
16
|
+
const res = await this.device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
17
|
+
eventless_wallet_session: true,
|
|
18
|
+
});
|
|
17
19
|
return Promise.resolve(res.message);
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -4,6 +4,8 @@ import type { Device, DeviceEvents } from '../device/Device';
|
|
|
4
4
|
|
|
5
5
|
export type HardwareUiEventHandlers = {
|
|
6
6
|
pin: (...event: DeviceEvents[typeof DEVICE.PIN]) => void;
|
|
7
|
+
pinOnDevice: (...event: DeviceEvents[typeof DEVICE.PIN_ON_DEVICE]) => void;
|
|
8
|
+
pinOnDeviceComplete: (...event: DeviceEvents[typeof DEVICE.PIN_ON_DEVICE_COMPLETE]) => void;
|
|
7
9
|
button: (...event: DeviceEvents[typeof DEVICE.BUTTON]) => void;
|
|
8
10
|
passphrase: (...event: DeviceEvents[typeof DEVICE.PASSPHRASE]) => void;
|
|
9
11
|
passphraseOnDevice: (...event: DeviceEvents[typeof DEVICE.PASSPHRASE_ON_DEVICE]) => void;
|
|
@@ -15,6 +17,8 @@ export function registerHardwareUiEventListeners(
|
|
|
15
17
|
handlers: HardwareUiEventHandlers
|
|
16
18
|
) {
|
|
17
19
|
device.on(DEVICE.PIN, handlers.pin);
|
|
20
|
+
device.on(DEVICE.PIN_ON_DEVICE, handlers.pinOnDevice);
|
|
21
|
+
device.on(DEVICE.PIN_ON_DEVICE_COMPLETE, handlers.pinOnDeviceComplete);
|
|
18
22
|
device.on(DEVICE.BUTTON, handlers.button);
|
|
19
23
|
device.on(DEVICE.PASSPHRASE, handlers.passphrase);
|
|
20
24
|
device.on(DEVICE.PASSPHRASE_ON_DEVICE, handlers.passphraseOnDevice);
|