@onekeyfe/hd-core 1.2.0-alpha.36 → 1.2.0-alpha.39
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 +5 -1
- package/__tests__/deviceUploadNft.test.ts +187 -0
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/open-wallet-session.test.ts +161 -44
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +86 -35
- package/__tests__/protocol-v2.test.ts +168 -444
- 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/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.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 +30 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +34 -2
- package/dist/index.js +544 -496
- package/dist/inject.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.map +1 -1
- package/dist/types/api/index.d.ts +2 -1
- 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/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 +29 -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 +7 -9
- package/src/api/OpenWalletSession.ts +37 -15
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +29 -30
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -7
- package/src/api/helpers/protocolV2FileWrite.ts +11 -4
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +166 -0
- package/src/core/index.ts +83 -82
- package/src/data/messages/messages-protocol-v2.json +10 -15
- package/src/events/logBlockEvent.ts +1 -0
- package/src/inject.ts +1 -0
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +117 -0
- package/src/protocols/protocol-v2/walletSession.ts +18 -51
- package/src/types/api/index.ts +2 -0
- package/src/types/api/protocolV2.ts +13 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +91 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- 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/src/api/firmware/progressThrottle.ts +0 -44
- package/src/protocols/protocol-v2/lockedError.ts +0 -10
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -131
|
@@ -5,6 +5,10 @@ import type {
|
|
|
5
5
|
DeviceUploadWallpaperParams,
|
|
6
6
|
DeviceUploadWallpaperResponse,
|
|
7
7
|
} from '../../api/protocol-v2/DeviceUploadWallpaper';
|
|
8
|
+
import type {
|
|
9
|
+
DeviceUploadNftParams,
|
|
10
|
+
DeviceUploadNftResponse,
|
|
11
|
+
} from '../../api/protocol-v2/DeviceUploadNft';
|
|
8
12
|
|
|
9
13
|
// Re-export implementation parameter types as the single source of truth.
|
|
10
14
|
export type { DeviceRebootParams, RebootTypeInput } from '../../api/protocol-v2/helpers';
|
|
@@ -12,6 +16,10 @@ export type {
|
|
|
12
16
|
DeviceUploadWallpaperParams,
|
|
13
17
|
DeviceUploadWallpaperResponse,
|
|
14
18
|
} from '../../api/protocol-v2/DeviceUploadWallpaper';
|
|
19
|
+
export type {
|
|
20
|
+
DeviceUploadNftParams,
|
|
21
|
+
DeviceUploadNftResponse,
|
|
22
|
+
} from '../../api/protocol-v2/DeviceUploadNft';
|
|
15
23
|
|
|
16
24
|
// ── Shared response shapes (Protocol V2 file system) ────────────────────
|
|
17
25
|
|
|
@@ -52,6 +60,11 @@ export declare function deviceUploadWallpaper(
|
|
|
52
60
|
params: CommonParams & DeviceUploadWallpaperParams
|
|
53
61
|
): Response<DeviceUploadWallpaperResponse>;
|
|
54
62
|
|
|
63
|
+
export declare function deviceUploadNft(
|
|
64
|
+
connectId: string,
|
|
65
|
+
params: CommonParams & DeviceUploadNftParams
|
|
66
|
+
): Response<DeviceUploadNftResponse>;
|
|
67
|
+
|
|
55
68
|
export declare function uploadPortfolio(
|
|
56
69
|
connectId: string,
|
|
57
70
|
params: {
|
package/src/utils/homescreen.ts
CHANGED
|
@@ -4,6 +4,12 @@ import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
|
4
4
|
import { getDeviceType } from './deviceInfoUtils';
|
|
5
5
|
import { getDeviceFirmwareVersion } from './deviceVersionUtils';
|
|
6
6
|
import { PRO2_WALLPAPER_HEIGHT, PRO2_WALLPAPER_WIDTH } from './pro2Wallpaper';
|
|
7
|
+
import {
|
|
8
|
+
PRO2_NFT_IMAGE_HEIGHT,
|
|
9
|
+
PRO2_NFT_IMAGE_WIDTH,
|
|
10
|
+
PRO2_NFT_THUMBNAIL_HEIGHT,
|
|
11
|
+
PRO2_NFT_THUMBNAIL_WIDTH,
|
|
12
|
+
} from './pro2Nft';
|
|
7
13
|
|
|
8
14
|
import type { Features, IDeviceType } from '../types';
|
|
9
15
|
|
|
@@ -302,6 +308,31 @@ type SizeConfig = {
|
|
|
302
308
|
radius?: number;
|
|
303
309
|
};
|
|
304
310
|
|
|
311
|
+
export const getNftSize = ({
|
|
312
|
+
deviceType,
|
|
313
|
+
thumbnail,
|
|
314
|
+
}: {
|
|
315
|
+
deviceType: IDeviceType;
|
|
316
|
+
thumbnail?: boolean;
|
|
317
|
+
}): SizeConfig | undefined => {
|
|
318
|
+
const sizes: Partial<Record<IDeviceType, { full: SizeConfig; thumbnail: SizeConfig }>> = {
|
|
319
|
+
touch: {
|
|
320
|
+
full: { width: 480, height: 800 },
|
|
321
|
+
thumbnail: { width: 238, height: 238 },
|
|
322
|
+
},
|
|
323
|
+
pro: {
|
|
324
|
+
full: { width: 480, height: 800 },
|
|
325
|
+
thumbnail: { width: 226, height: 226, radius: 40 },
|
|
326
|
+
},
|
|
327
|
+
pro2: {
|
|
328
|
+
full: { width: PRO2_NFT_IMAGE_WIDTH, height: PRO2_NFT_IMAGE_HEIGHT },
|
|
329
|
+
thumbnail: { width: PRO2_NFT_THUMBNAIL_WIDTH, height: PRO2_NFT_THUMBNAIL_HEIGHT },
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
return sizes[deviceType]?.[thumbnail ? 'thumbnail' : 'full'];
|
|
334
|
+
};
|
|
335
|
+
|
|
305
336
|
export const getHomeScreenSize = ({
|
|
306
337
|
deviceType,
|
|
307
338
|
homeScreenType,
|
|
@@ -312,12 +343,7 @@ export const getHomeScreenSize = ({
|
|
|
312
343
|
thumbnail?: boolean;
|
|
313
344
|
}) => {
|
|
314
345
|
if (deviceType === EDeviceType.Pro2) {
|
|
315
|
-
return thumbnail
|
|
316
|
-
? undefined
|
|
317
|
-
: {
|
|
318
|
-
width: PRO2_WALLPAPER_WIDTH,
|
|
319
|
-
height: PRO2_WALLPAPER_HEIGHT,
|
|
320
|
-
};
|
|
346
|
+
return thumbnail ? undefined : { width: PRO2_WALLPAPER_WIDTH, height: PRO2_WALLPAPER_HEIGHT };
|
|
321
347
|
}
|
|
322
348
|
|
|
323
349
|
const sizes: Partial<
|
package/src/utils/index.ts
CHANGED
|
@@ -54,7 +54,12 @@ export { getHDPath, getScriptType, getOutputScriptType } from '../api/helpers/pa
|
|
|
54
54
|
|
|
55
55
|
export const isBleConnect = (env: string) => env === 'react-native' || env === 'lowlevel';
|
|
56
56
|
|
|
57
|
-
export {
|
|
57
|
+
export {
|
|
58
|
+
getHomeScreenHex,
|
|
59
|
+
getHomeScreenDefaultList,
|
|
60
|
+
getHomeScreenSize,
|
|
61
|
+
getNftSize,
|
|
62
|
+
} from './homescreen';
|
|
58
63
|
export { encodePro2Wallpaper, PRO2_WALLPAPER_HEIGHT, PRO2_WALLPAPER_WIDTH } from './pro2Wallpaper';
|
|
59
64
|
export type { Pro2WallpaperColorFormat } from './pro2Wallpaper';
|
|
60
65
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { blake2s } from '@noble/hashes/blake2s';
|
|
2
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
|
+
|
|
4
|
+
import { invalidParameter } from '../api/helpers/filesystemValidation';
|
|
5
|
+
import { encodePro2Image } from './pro2Wallpaper';
|
|
6
|
+
|
|
7
|
+
export const PRO2_NFT_IMAGE_WIDTH = 540;
|
|
8
|
+
export const PRO2_NFT_IMAGE_HEIGHT = 540;
|
|
9
|
+
export const PRO2_NFT_THUMBNAIL_WIDTH = 263;
|
|
10
|
+
export const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
|
|
11
|
+
export const PRO2_NFT_DIRECTORY = 'vol1:/nft';
|
|
12
|
+
export const PRO2_NFT_DEFAULT_CHUNK_SIZE = 512;
|
|
13
|
+
export const PRO2_NFT_DEFAULT_PACE_MS = 20;
|
|
14
|
+
export const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15_000;
|
|
15
|
+
export const PRO2_NFT_MIN_CHUNK_SIZE = 64;
|
|
16
|
+
export const PRO2_NFT_MAX_CHUNK_SIZE = 2048;
|
|
17
|
+
|
|
18
|
+
export type Pro2NftImage = {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
rgba: Uint8Array | ArrayBuffer;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type Pro2NftBundle = {
|
|
25
|
+
basename: string;
|
|
26
|
+
image: Uint8Array;
|
|
27
|
+
thumbnail: Uint8Array;
|
|
28
|
+
metadata: Uint8Array;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function utf8Length(value: string): number {
|
|
32
|
+
return new TextEncoder().encode(value).byteLength;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertImage(
|
|
36
|
+
name: string,
|
|
37
|
+
image: Pro2NftImage,
|
|
38
|
+
expectedWidth: number,
|
|
39
|
+
expectedHeight: number
|
|
40
|
+
) {
|
|
41
|
+
if (image.width !== expectedWidth || image.height !== expectedHeight) {
|
|
42
|
+
throw invalidParameter(
|
|
43
|
+
`Pro2 NFT ${name} dimensions must be ${expectedWidth}x${expectedHeight}.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (!(image.rgba instanceof ArrayBuffer) && !ArrayBuffer.isView(image.rgba)) {
|
|
47
|
+
throw invalidParameter(`Parameter [${name}.rgba] must be an ArrayBuffer or Uint8Array.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function buildPro2NftBundle(options: {
|
|
52
|
+
image: Pro2NftImage;
|
|
53
|
+
thumbnail: Pro2NftImage;
|
|
54
|
+
title: string;
|
|
55
|
+
subtitle: string;
|
|
56
|
+
timestampMs: number;
|
|
57
|
+
}): Pro2NftBundle {
|
|
58
|
+
const { image, thumbnail, title, subtitle, timestampMs } = options;
|
|
59
|
+
assertImage('image', image, PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT);
|
|
60
|
+
assertImage('thumbnail', thumbnail, PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT);
|
|
61
|
+
const titleLength = typeof title === 'string' ? utf8Length(title) : 0;
|
|
62
|
+
const subtitleLength =
|
|
63
|
+
typeof subtitle === 'string' ? utf8Length(subtitle) : Number.POSITIVE_INFINITY;
|
|
64
|
+
if (titleLength < 1 || titleLength > 63) {
|
|
65
|
+
throw invalidParameter('Pro2 NFT title must contain 1 to 63 UTF-8 bytes.');
|
|
66
|
+
}
|
|
67
|
+
if (subtitleLength > 95) {
|
|
68
|
+
throw invalidParameter('Pro2 NFT subtitle must contain at most 95 UTF-8 bytes.');
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isSafeInteger(timestampMs) || timestampMs <= 0) {
|
|
71
|
+
throw invalidParameter('Parameter [timestampMs] must be a positive safe integer.');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
|
|
75
|
+
const encodedThumbnail = encodePro2Image({
|
|
76
|
+
...thumbnail,
|
|
77
|
+
alphaMode: 'black-background',
|
|
78
|
+
}).data;
|
|
79
|
+
const metadata = new TextEncoder().encode(JSON.stringify({ title, subtitle }));
|
|
80
|
+
if (metadata.byteLength === 0 || metadata.byteLength > 512) {
|
|
81
|
+
throw invalidParameter('Pro2 NFT metadata must contain 1 to 512 UTF-8 bytes.');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const hash8 = bytesToHex(blake2s(encodedImage)).slice(0, 8);
|
|
85
|
+
return {
|
|
86
|
+
basename: `nft-${hash8}-${timestampMs}`,
|
|
87
|
+
image: encodedImage,
|
|
88
|
+
thumbnail: encodedThumbnail,
|
|
89
|
+
metadata,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -8,6 +8,8 @@ export const PRO2_WALLPAPER_HEIGHT = 1024;
|
|
|
8
8
|
|
|
9
9
|
export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8';
|
|
10
10
|
|
|
11
|
+
export type Pro2ImageAlphaMode = 'preserve' | 'black-background';
|
|
12
|
+
|
|
11
13
|
const COLOR_FORMAT_RGB565 = 0x12;
|
|
12
14
|
const COLOR_FORMAT_RGB565A8 = 0x14;
|
|
13
15
|
|
|
@@ -36,10 +38,11 @@ function align(value: number, boundary: number): number {
|
|
|
36
38
|
return Math.ceil(value / boundary) * boundary;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
export function
|
|
41
|
+
export function encodePro2Image(options: {
|
|
40
42
|
width: number;
|
|
41
43
|
height: number;
|
|
42
44
|
rgba: Uint8Array | ArrayBuffer;
|
|
45
|
+
alphaMode?: Pro2ImageAlphaMode;
|
|
43
46
|
}): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
|
|
44
47
|
const { width, height } = options;
|
|
45
48
|
if (!Number.isInteger(width) || width <= 0 || width > 0xffff) {
|
|
@@ -57,11 +60,14 @@ export function encodePro2Wallpaper(options: {
|
|
|
57
60
|
);
|
|
58
61
|
}
|
|
59
62
|
|
|
63
|
+
const alphaMode = options.alphaMode ?? 'preserve';
|
|
60
64
|
let hasTransparency = false;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
if (alphaMode === 'preserve') {
|
|
66
|
+
for (let index = 3; index < rgba.length; index += 4) {
|
|
67
|
+
if (rgba[index] !== 0xff) {
|
|
68
|
+
hasTransparency = true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
|
|
@@ -85,9 +91,22 @@ export function encodePro2Wallpaper(options: {
|
|
|
85
91
|
for (let x = 0; x < width; x += 1) {
|
|
86
92
|
const sourceOffset = (y * width + x) * 4;
|
|
87
93
|
const thresholdIndex = ((y & 7) << 3) + (x & 7);
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
94
|
+
const alpha = rgba[sourceOffset + 3];
|
|
95
|
+
const redChannel =
|
|
96
|
+
alphaMode === 'black-background'
|
|
97
|
+
? Math.round((rgba[sourceOffset] * alpha) / 0xff)
|
|
98
|
+
: rgba[sourceOffset];
|
|
99
|
+
const greenChannel =
|
|
100
|
+
alphaMode === 'black-background'
|
|
101
|
+
? Math.round((rgba[sourceOffset + 1] * alpha) / 0xff)
|
|
102
|
+
: rgba[sourceOffset + 1];
|
|
103
|
+
const blueChannel =
|
|
104
|
+
alphaMode === 'black-background'
|
|
105
|
+
? Math.round((rgba[sourceOffset + 2] * alpha) / 0xff)
|
|
106
|
+
: rgba[sourceOffset + 2];
|
|
107
|
+
const red = Math.min(redChannel + RED_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
|
|
108
|
+
const green = Math.min(greenChannel + GREEN_THRESHOLD[thresholdIndex], 0xff) & 0xfc;
|
|
109
|
+
const blue = Math.min(blueChannel + BLUE_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
|
|
91
110
|
const rgb565 = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
|
|
92
111
|
const rgbOffset = 12 + y * stride + x * 2;
|
|
93
112
|
data[rgbOffset] = rgb565 & 0xff;
|
|
@@ -101,3 +120,11 @@ export function encodePro2Wallpaper(options: {
|
|
|
101
120
|
|
|
102
121
|
return { data, colorFormat };
|
|
103
122
|
}
|
|
123
|
+
|
|
124
|
+
export function encodePro2Wallpaper(options: {
|
|
125
|
+
width: number;
|
|
126
|
+
height: number;
|
|
127
|
+
rgba: Uint8Array | ArrayBuffer;
|
|
128
|
+
}): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
|
|
129
|
+
return encodePro2Image(options);
|
|
130
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { UI_EVENT, UI_REQUEST } from '../events/ui-request';
|
|
2
|
+
|
|
3
|
+
import type { CoreMessage } from '../events';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_UI_PROGRESS_INTERVAL_MS = 250;
|
|
6
|
+
|
|
7
|
+
type UiProgressThrottleState = {
|
|
8
|
+
lastEmittedAt: number;
|
|
9
|
+
lastProgress?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const normalizeProgress = (progress: unknown) => {
|
|
13
|
+
if (typeof progress !== 'number' || !Number.isFinite(progress)) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return Math.max(0, Math.min(100, Math.floor(progress)));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const createUiProgressMessageFilter = (
|
|
21
|
+
intervalMs = DEFAULT_UI_PROGRESS_INTERVAL_MS,
|
|
22
|
+
now = Date.now
|
|
23
|
+
) => {
|
|
24
|
+
const stateByType = new Map<string, UiProgressThrottleState>();
|
|
25
|
+
|
|
26
|
+
return (message: CoreMessage) => {
|
|
27
|
+
if (
|
|
28
|
+
!('event' in message) ||
|
|
29
|
+
message.event !== UI_EVENT ||
|
|
30
|
+
(message.type !== UI_REQUEST.DEVICE_PROGRESS && message.type !== UI_REQUEST.FIRMWARE_PROGRESS)
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const key =
|
|
36
|
+
message.type === UI_REQUEST.FIRMWARE_PROGRESS
|
|
37
|
+
? `${message.type}:${message.payload.progressType}`
|
|
38
|
+
: message.type;
|
|
39
|
+
const progress = normalizeProgress(message.payload.progress);
|
|
40
|
+
const currentTime = now();
|
|
41
|
+
const state = stateByType.get(key);
|
|
42
|
+
|
|
43
|
+
if (!state) {
|
|
44
|
+
stateByType.set(key, { lastEmittedAt: currentTime, lastProgress: progress });
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isBoundary = progress === 0 || progress === 100;
|
|
49
|
+
if (isBoundary && progress === state.lastProgress) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const startsNewPhase =
|
|
54
|
+
progress !== undefined && state.lastProgress !== undefined && progress < state.lastProgress;
|
|
55
|
+
|
|
56
|
+
if (!isBoundary && !startsNewPhase && currentTime - state.lastEmittedAt < intervalMs) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
stateByType.set(key, { lastEmittedAt: currentTime, lastProgress: progress });
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { IFirmwareUpdateProgressType } from '../../events/ui-request';
|
|
2
|
-
export declare const createFirmwareProgressThrottle: (intervalMs?: number) => (progress: number, progressType: IFirmwareUpdateProgressType) => boolean;
|
|
3
|
-
//# sourceMappingURL=progressThrottle.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"progressThrottle.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/progressThrottle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAS3E,eAAO,MAAM,8BAA8B,sCAKvB,MAAM,gBAAgB,2BAA2B,YA6BpE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lockedError.d.ts","sourceRoot":"","sources":["../../../src/protocols/protocol-v2/lockedError.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAO3D"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { BaseMethod } from '../../api/BaseMethod';
|
|
2
|
-
import type { Device } from '../../device/Device';
|
|
3
|
-
import type { ProtocolV2UiInteractionCoordinator } from './uiInteraction';
|
|
4
|
-
type RunnableMethod = Pick<BaseMethod, 'run' | 'unlockPolicy' | 'protocolV2UiInteraction' | 'protocolV2UiMode' | 'params' | 'payload' | 'useDevicePassphraseState'> & {
|
|
5
|
-
name?: string;
|
|
6
|
-
};
|
|
7
|
-
type UiInteractionCoordinator = Pick<ProtocolV2UiInteractionCoordinator, 'enterMethodInteraction' | 'enterUnlockInteraction' | 'resumeMethodInteraction'>;
|
|
8
|
-
export declare function runMethodWithUnlockRetry(method: RunnableMethod, device: Device, uiCoordinator?: UiInteractionCoordinator): Promise<any>;
|
|
9
|
-
export {};
|
|
10
|
-
//# sourceMappingURL=unlockRetry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unlockRetry.d.ts","sourceRoot":"","sources":["../../../src/protocols/protocol-v2/unlockRetry.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,iBAAiB,CAAC;AAI1E,KAAK,cAAc,GAAG,IAAI,CACxB,UAAU,EACR,KAAK,GACL,cAAc,GACd,yBAAyB,GACzB,kBAAkB,GAClB,QAAQ,GACR,SAAS,GACT,0BAA0B,CAC7B,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACtB,KAAK,wBAAwB,GAAG,IAAI,CAClC,kCAAkC,EAClC,wBAAwB,GAAG,wBAAwB,GAAG,yBAAyB,CAChF,CAAC;AAkBF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,wBAAwB,gBAoFzC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { IFirmwareUpdateProgressType } from '../../events/ui-request';
|
|
2
|
-
|
|
3
|
-
const DEFAULT_FIRMWARE_PROGRESS_INTERVAL_MS = 250;
|
|
4
|
-
|
|
5
|
-
type FirmwareProgressThrottleState = {
|
|
6
|
-
lastEmittedAt: number;
|
|
7
|
-
lastProgress?: number;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const createFirmwareProgressThrottle = (
|
|
11
|
-
intervalMs = DEFAULT_FIRMWARE_PROGRESS_INTERVAL_MS
|
|
12
|
-
) => {
|
|
13
|
-
const stateByType = new Map<IFirmwareUpdateProgressType, FirmwareProgressThrottleState>();
|
|
14
|
-
|
|
15
|
-
return (progress: number, progressType: IFirmwareUpdateProgressType) => {
|
|
16
|
-
if (progressType !== 'transferData') {
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const normalizedProgress = Math.max(0, Math.min(100, Math.floor(progress)));
|
|
21
|
-
if (normalizedProgress === 0 || normalizedProgress === 100) {
|
|
22
|
-
stateByType.set(progressType, {
|
|
23
|
-
lastEmittedAt: Date.now(),
|
|
24
|
-
lastProgress: normalizedProgress,
|
|
25
|
-
});
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const now = Date.now();
|
|
30
|
-
const state = stateByType.get(progressType);
|
|
31
|
-
if (
|
|
32
|
-
state &&
|
|
33
|
-
(state.lastProgress === normalizedProgress || now - state.lastEmittedAt < intervalMs)
|
|
34
|
-
) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
stateByType.set(progressType, {
|
|
39
|
-
lastEmittedAt: now,
|
|
40
|
-
lastProgress: normalizedProgress,
|
|
41
|
-
});
|
|
42
|
-
return true;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
|
|
3
|
-
export function isDeviceLockedError(error: unknown): boolean {
|
|
4
|
-
return (
|
|
5
|
-
typeof error === 'object' &&
|
|
6
|
-
error !== null &&
|
|
7
|
-
'errorCode' in error &&
|
|
8
|
-
error.errorCode === HardwareErrorCode.DeviceLocked
|
|
9
|
-
);
|
|
10
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { LoggerNames, getLogger } from '../../utils';
|
|
2
|
-
import { isDeviceLockedError } from './lockedError';
|
|
3
|
-
import { isProtocolV2UiEnabled, resolveProtocolV2UiInteraction } from './uiInteraction';
|
|
4
|
-
import { refreshProtocolV2DeviceStatus, restoreProtocolV2WalletSession } from './walletSession';
|
|
5
|
-
|
|
6
|
-
import type { BaseMethod } from '../../api/BaseMethod';
|
|
7
|
-
import type { Device } from '../../device/Device';
|
|
8
|
-
import type { HardwareUiInteractionMeta } from '../../events';
|
|
9
|
-
import type { ProtocolV2UiInteractionCoordinator } from './uiInteraction';
|
|
10
|
-
|
|
11
|
-
const Log = getLogger(LoggerNames.Core);
|
|
12
|
-
|
|
13
|
-
type RunnableMethod = Pick<
|
|
14
|
-
BaseMethod,
|
|
15
|
-
| 'run'
|
|
16
|
-
| 'unlockPolicy'
|
|
17
|
-
| 'protocolV2UiInteraction'
|
|
18
|
-
| 'protocolV2UiMode'
|
|
19
|
-
| 'params'
|
|
20
|
-
| 'payload'
|
|
21
|
-
| 'useDevicePassphraseState'
|
|
22
|
-
> & { name?: string };
|
|
23
|
-
type UiInteractionCoordinator = Pick<
|
|
24
|
-
ProtocolV2UiInteractionCoordinator,
|
|
25
|
-
'enterMethodInteraction' | 'enterUnlockInteraction' | 'resumeMethodInteraction'
|
|
26
|
-
>;
|
|
27
|
-
|
|
28
|
-
const restoreExpectedWalletSessionAfterUnlock = async (method: RunnableMethod, device: Device) => {
|
|
29
|
-
const expectedPassphraseState = method.payload?.useEmptyPassphrase
|
|
30
|
-
? undefined
|
|
31
|
-
: method.payload?.passphraseState ?? device.passphraseState;
|
|
32
|
-
if (
|
|
33
|
-
!method.useDevicePassphraseState ||
|
|
34
|
-
typeof expectedPassphraseState !== 'string' ||
|
|
35
|
-
expectedPassphraseState.length === 0
|
|
36
|
-
) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
await restoreProtocolV2WalletSession(device, expectedPassphraseState);
|
|
41
|
-
Log.debug('Protocol V2 wallet session restored after unlock', { method: method.name });
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export async function runMethodWithUnlockRetry(
|
|
45
|
-
method: RunnableMethod,
|
|
46
|
-
device: Device,
|
|
47
|
-
uiCoordinator?: UiInteractionCoordinator
|
|
48
|
-
) {
|
|
49
|
-
const shouldEmitUi = isProtocolV2UiEnabled(method);
|
|
50
|
-
const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
|
|
51
|
-
const isProtocolV2 = device.isProtocolV2();
|
|
52
|
-
const requiresFreshStatus =
|
|
53
|
-
isProtocolV2 &&
|
|
54
|
-
method.unlockPolicy === 'unlock-before-run' &&
|
|
55
|
-
!device.isBootloader?.() &&
|
|
56
|
-
!device.isRomloader?.();
|
|
57
|
-
|
|
58
|
-
if (requiresFreshStatus) {
|
|
59
|
-
await refreshProtocolV2DeviceStatus(device);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const shouldUnlockBeforeRun =
|
|
63
|
-
isProtocolV2 &&
|
|
64
|
-
method.unlockPolicy !== 'none' &&
|
|
65
|
-
!device.isBootloader?.() &&
|
|
66
|
-
!device.isRomloader?.() &&
|
|
67
|
-
device.features?.unlocked === false;
|
|
68
|
-
|
|
69
|
-
if (shouldUnlockBeforeRun) {
|
|
70
|
-
const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
|
|
71
|
-
? uiCoordinator?.enterUnlockInteraction(method.name)
|
|
72
|
-
: undefined;
|
|
73
|
-
await device.unlockDevice(
|
|
74
|
-
undefined,
|
|
75
|
-
shouldCoordinateUi
|
|
76
|
-
? { emitUiEvent: false, interaction: unlockInteraction }
|
|
77
|
-
: {
|
|
78
|
-
source: 'unlock-coordinator',
|
|
79
|
-
reason: 'device-locked',
|
|
80
|
-
deviceOnly: true,
|
|
81
|
-
method: method.name,
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
Log.debug('Protocol V2 pre-unlock completed', { method: method.name });
|
|
85
|
-
await restoreExpectedWalletSessionAfterUnlock(method, device);
|
|
86
|
-
if (shouldCoordinateUi) {
|
|
87
|
-
uiCoordinator?.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
|
|
88
|
-
}
|
|
89
|
-
return method.run();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (shouldCoordinateUi) {
|
|
93
|
-
uiCoordinator?.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
|
|
94
|
-
}
|
|
95
|
-
try {
|
|
96
|
-
return await method.run();
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (!isProtocolV2 || method.unlockPolicy !== 'retry-on-locked' || !isDeviceLockedError(error)) {
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
Log.debug('Protocol V2 unlock retry triggered', { method: method.name });
|
|
103
|
-
const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
|
|
104
|
-
? uiCoordinator?.enterUnlockInteraction(method.name)
|
|
105
|
-
: undefined;
|
|
106
|
-
await device.unlockDevice(
|
|
107
|
-
undefined,
|
|
108
|
-
shouldCoordinateUi
|
|
109
|
-
? { emitUiEvent: false, interaction: unlockInteraction }
|
|
110
|
-
: {
|
|
111
|
-
source: 'unlock-coordinator',
|
|
112
|
-
reason: 'device-locked',
|
|
113
|
-
deviceOnly: true,
|
|
114
|
-
method: method.name,
|
|
115
|
-
}
|
|
116
|
-
);
|
|
117
|
-
Log.debug('Protocol V2 unlock completed', { method: method.name });
|
|
118
|
-
await restoreExpectedWalletSessionAfterUnlock(method, device);
|
|
119
|
-
if (shouldCoordinateUi) {
|
|
120
|
-
uiCoordinator?.resumeMethodInteraction();
|
|
121
|
-
}
|
|
122
|
-
try {
|
|
123
|
-
const response = await method.run();
|
|
124
|
-
Log.debug('Protocol V2 method retry completed', { method: method.name, success: true });
|
|
125
|
-
return response;
|
|
126
|
-
} catch (retryError) {
|
|
127
|
-
Log.debug('Protocol V2 method retry completed', { method: method.name, success: false });
|
|
128
|
-
throw retryError;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|