@onekeyfe/hd-core 1.2.0-alpha.32 → 1.2.0-alpha.34
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__/check-all-firmware-release-protocol-v2.test.ts +31 -0
- package/__tests__/core-dispose.test.ts +3 -3
- package/__tests__/device-lifecycle-events.test.ts +52 -0
- package/__tests__/device-pool-state.test.ts +14 -2
- package/__tests__/device-settings.test.ts +22 -0
- package/__tests__/device-state-mapper.test.ts +13 -2
- package/__tests__/device-state-projector.test.ts +2 -2
- package/__tests__/device-utils.test.ts +1 -0
- package/__tests__/device-wallet-session-store.test.ts +64 -17
- package/__tests__/kaspaSignTransaction.test.ts +9 -0
- package/__tests__/known-device-public-types.type-test.ts +3 -0
- package/__tests__/logBlockEvent.test.ts +89 -5
- package/__tests__/open-wallet-session.test.ts +334 -30
- package/__tests__/pro2Wallpaper.test.ts +1 -1
- package/__tests__/protocol-v2.test.ts +708 -75
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +7 -2
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts +1 -0
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/device/DeviceStateProjector.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +8 -0
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
- package/dist/events/logBlockEvent.d.ts +2 -0
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +19 -7
- package/dist/index.js +456 -222
- package/dist/protocols/protocol-v2/walletSession.d.ts +0 -10
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -1
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/device.d.ts +4 -2
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +0 -5
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +11 -81
- package/src/api/FirmwareUpdateV4.ts +33 -17
- package/src/api/GetPassphraseState.ts +1 -3
- package/src/api/OpenWalletSession.ts +3 -3
- package/src/api/device/DeviceSettings.ts +8 -1
- package/src/api/kaspa/KaspaSignTransaction.ts +7 -0
- package/src/core/index.ts +7 -5
- package/src/device/Device.ts +37 -9
- package/src/device/DeviceConnector.ts +10 -7
- package/src/device/DevicePool.ts +5 -3
- package/src/device/DeviceStateMapper.ts +5 -3
- package/src/device/DeviceStateProjector.ts +0 -7
- package/src/device/DeviceWalletSessionStore.ts +57 -4
- package/src/deviceProfile/buildDeviceFeatures.ts +10 -3
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +32 -0
- package/src/events/logBlockEvent.ts +84 -1
- package/src/protocols/protocol-v2/deviceSession.ts +1 -1
- package/src/protocols/protocol-v2/walletSession.ts +116 -34
- package/src/types/api/openWalletSession.ts +1 -1
- package/src/types/device.ts +6 -2
- package/src/types/params.ts +2 -1
- package/src/utils/deviceFeaturesCompat.ts +8 -1
- package/src/utils/pro2Wallpaper.ts +3 -9
- package/tsconfig.type-tests.json +5 -1
|
@@ -8,6 +8,21 @@ import { isDeviceLockedError } from './lockedError';
|
|
|
8
8
|
import type { DeviceSessionGet } from '@onekeyfe/hd-transport';
|
|
9
9
|
import type { Device } from '../../device/Device';
|
|
10
10
|
|
|
11
|
+
const HOST_PASSPHRASE_MAX_BYTES = 50;
|
|
12
|
+
|
|
13
|
+
const utf8ByteLength = (value: string): number | undefined => {
|
|
14
|
+
let length = 0;
|
|
15
|
+
for (const character of value) {
|
|
16
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
17
|
+
if (codePoint >= 0xd800 && codePoint <= 0xdfff) return undefined;
|
|
18
|
+
if (codePoint <= 0x7f) length += 1;
|
|
19
|
+
else if (codePoint <= 0x7ff) length += 2;
|
|
20
|
+
else if (codePoint <= 0xffff) length += 3;
|
|
21
|
+
else length += 4;
|
|
22
|
+
}
|
|
23
|
+
return length;
|
|
24
|
+
};
|
|
25
|
+
|
|
11
26
|
export async function requestProtocolV2DeviceStatus(device: Device) {
|
|
12
27
|
const { message } = await device.commands.typedCall('DeviceStatusGet', 'DeviceStatus', {});
|
|
13
28
|
return message;
|
|
@@ -28,7 +43,7 @@ const getDeviceSession = async (device: Device, request: DeviceSessionGet) => {
|
|
|
28
43
|
try {
|
|
29
44
|
return await device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
|
|
30
45
|
} catch (error) {
|
|
31
|
-
if (!isDeviceLockedError(error)) {
|
|
46
|
+
if (!isDeviceLockedError(error) || !request.session_id) {
|
|
32
47
|
throw error;
|
|
33
48
|
}
|
|
34
49
|
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
@@ -44,32 +59,47 @@ const askDevicePassphrase = async (device: Device, passphrase?: string) => {
|
|
|
44
59
|
passphrase ? { passphrase, on_device: false } : { on_device: true }
|
|
45
60
|
);
|
|
46
61
|
try {
|
|
47
|
-
|
|
62
|
+
await request();
|
|
48
63
|
} catch (error) {
|
|
49
64
|
if (!isDeviceLockedError(error)) {
|
|
50
65
|
throw error;
|
|
51
66
|
}
|
|
52
67
|
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
53
|
-
|
|
68
|
+
await request();
|
|
54
69
|
}
|
|
70
|
+
await refreshProtocolV2DeviceStatus(device);
|
|
55
71
|
};
|
|
56
72
|
|
|
57
|
-
const selectDeviceSession = async (device: Device) => {
|
|
73
|
+
const selectDeviceSession = async (device: Device, expectedPassphraseState?: string) => {
|
|
58
74
|
const existsAttachPinUser = device.features?.attachToPinEnabled === true;
|
|
59
75
|
const metadata = {
|
|
60
76
|
source: 'wallet-session-coordinator' as const,
|
|
61
|
-
reason: 'open-wallet' as const,
|
|
77
|
+
reason: expectedPassphraseState ? ('session-recovery' as const) : ('open-wallet' as const),
|
|
78
|
+
...(expectedPassphraseState ? { expectedPassphraseState } : {}),
|
|
62
79
|
};
|
|
63
80
|
const response = await device.commands.promptPassphrase(
|
|
64
81
|
{
|
|
65
82
|
existsAttachPinUser,
|
|
66
|
-
deviceOnly:
|
|
83
|
+
deviceOnly: false,
|
|
67
84
|
...metadata,
|
|
68
85
|
},
|
|
69
86
|
{ cancelDeviceOnReject: false }
|
|
70
87
|
);
|
|
71
|
-
const
|
|
72
|
-
typeof response.passphrase === 'string'
|
|
88
|
+
const hostPassphrase =
|
|
89
|
+
typeof response.passphrase === 'string' ? response.passphrase.normalize('NFKD') : undefined;
|
|
90
|
+
const hasHostPassphrase = typeof hostPassphrase === 'string' && hostPassphrase.length > 0;
|
|
91
|
+
const hostPassphraseByteLength = hasHostPassphrase ? utf8ByteLength(hostPassphrase) : undefined;
|
|
92
|
+
if (
|
|
93
|
+
hasHostPassphrase &&
|
|
94
|
+
(hostPassphrase.includes('\0') ||
|
|
95
|
+
hostPassphraseByteLength === undefined ||
|
|
96
|
+
hostPassphraseByteLength > HOST_PASSPHRASE_MAX_BYTES)
|
|
97
|
+
) {
|
|
98
|
+
throw ERRORS.TypedError(
|
|
99
|
+
HardwareErrorCode.RuntimeError,
|
|
100
|
+
`Host passphrase must contain 1 to ${HOST_PASSPHRASE_MAX_BYTES} valid UTF-8 bytes without NUL.`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
73
103
|
const selections = [
|
|
74
104
|
hasHostPassphrase,
|
|
75
105
|
response.passphraseOnDevice === true,
|
|
@@ -96,7 +126,7 @@ const selectDeviceSession = async (device: Device) => {
|
|
|
96
126
|
}
|
|
97
127
|
|
|
98
128
|
if (hasHostPassphrase) {
|
|
99
|
-
await askDevicePassphrase(device,
|
|
129
|
+
await askDevicePassphrase(device, hostPassphrase);
|
|
100
130
|
return getDeviceSession(device, {});
|
|
101
131
|
}
|
|
102
132
|
|
|
@@ -115,7 +145,11 @@ export async function getProtocolV2WalletSession(
|
|
|
115
145
|
}
|
|
116
146
|
) {
|
|
117
147
|
if (options?.initSession) {
|
|
118
|
-
|
|
148
|
+
if (options.onlyMainPin) {
|
|
149
|
+
device.clearStandardInternalState?.();
|
|
150
|
+
} else {
|
|
151
|
+
device.clearInternalState();
|
|
152
|
+
}
|
|
119
153
|
device.passphraseState = undefined;
|
|
120
154
|
}
|
|
121
155
|
|
|
@@ -124,29 +158,42 @@ export async function getProtocolV2WalletSession(
|
|
|
124
158
|
if (options?.onlyMainPin) {
|
|
125
159
|
device.passphraseState = undefined;
|
|
126
160
|
}
|
|
127
|
-
|
|
161
|
+
let expectedPassphraseState = options?.onlyMainPin
|
|
128
162
|
? undefined
|
|
129
163
|
: options?.expectedPassphraseState ?? device.passphraseState;
|
|
130
164
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (options?.onlyMainPin || device.getCurrentPassphraseProtection() === false) {
|
|
136
|
-
return {
|
|
137
|
-
passphraseState: undefined,
|
|
138
|
-
newSession: undefined,
|
|
139
|
-
unlockedAttachPin: device.features?.unlockedAttachPin ?? false,
|
|
140
|
-
resumed: false,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
165
|
+
const cachedStandardSession = options?.onlyMainPin
|
|
166
|
+
? device.getStandardInternalState?.()
|
|
167
|
+
: undefined;
|
|
144
168
|
const cachedSessionId =
|
|
145
|
-
typeof device.getInternalState === 'function'
|
|
169
|
+
!options?.onlyMainPin && typeof device.getInternalState === 'function'
|
|
170
|
+
? device.getInternalState()
|
|
171
|
+
: undefined;
|
|
146
172
|
let response;
|
|
147
173
|
let resumed = false;
|
|
148
174
|
|
|
149
|
-
if (
|
|
175
|
+
if (options?.onlyMainPin) {
|
|
176
|
+
expectedPassphraseState = cachedStandardSession?.passphraseState;
|
|
177
|
+
if (cachedStandardSession) {
|
|
178
|
+
try {
|
|
179
|
+
if (device.features?.unlockedAttachPin === true) {
|
|
180
|
+
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
181
|
+
}
|
|
182
|
+
response = await getDeviceSession(device, {
|
|
183
|
+
session_id: cachedStandardSession.sessionId,
|
|
184
|
+
});
|
|
185
|
+
resumed = true;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
device.clearStandardInternalState?.();
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!response) {
|
|
193
|
+
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
194
|
+
response = await getDeviceSession(device, {});
|
|
195
|
+
}
|
|
196
|
+
} else if (cachedSessionId && expectedPassphraseState) {
|
|
150
197
|
try {
|
|
151
198
|
response = await getDeviceSession(device, { session_id: cachedSessionId });
|
|
152
199
|
resumed = true;
|
|
@@ -161,28 +208,63 @@ export async function getProtocolV2WalletSession(
|
|
|
161
208
|
device.clearInternalState();
|
|
162
209
|
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
163
210
|
}
|
|
164
|
-
response = await selectDeviceSession(device);
|
|
211
|
+
response = await selectDeviceSession(device, expectedPassphraseState);
|
|
165
212
|
}
|
|
166
213
|
|
|
167
|
-
|
|
214
|
+
let { message } = response;
|
|
168
215
|
try {
|
|
169
216
|
assertCompleteDeviceSession(message);
|
|
170
217
|
} catch (error) {
|
|
171
|
-
|
|
218
|
+
if (options?.onlyMainPin) {
|
|
219
|
+
device.clearStandardInternalState?.();
|
|
220
|
+
} else {
|
|
221
|
+
device.clearInternalState();
|
|
222
|
+
}
|
|
172
223
|
throw error;
|
|
173
224
|
}
|
|
174
225
|
if (expectedPassphraseState && expectedPassphraseState !== message.btc_test_address) {
|
|
175
|
-
|
|
176
|
-
|
|
226
|
+
resumed = false;
|
|
227
|
+
if (options?.onlyMainPin) {
|
|
228
|
+
device.clearStandardInternalState?.();
|
|
229
|
+
await device.unlockDevice(DeviceSessionPinType.Main);
|
|
230
|
+
response = await getDeviceSession(device, {});
|
|
231
|
+
} else {
|
|
232
|
+
device.clearInternalState();
|
|
233
|
+
response = await selectDeviceSession(device, expectedPassphraseState);
|
|
234
|
+
}
|
|
235
|
+
message = response.message;
|
|
236
|
+
try {
|
|
237
|
+
assertCompleteDeviceSession(message);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (options?.onlyMainPin) {
|
|
240
|
+
device.clearStandardInternalState?.();
|
|
241
|
+
} else {
|
|
242
|
+
device.clearInternalState();
|
|
243
|
+
}
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
if (expectedPassphraseState !== message.btc_test_address) {
|
|
247
|
+
if (options?.onlyMainPin) {
|
|
248
|
+
device.clearStandardInternalState?.();
|
|
249
|
+
} else {
|
|
250
|
+
device.clearInternalState();
|
|
251
|
+
}
|
|
252
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
253
|
+
}
|
|
177
254
|
}
|
|
178
255
|
|
|
179
|
-
|
|
256
|
+
const internalStateArgs = [
|
|
180
257
|
true,
|
|
181
258
|
message.btc_test_address,
|
|
182
259
|
device.getCurrentDeviceId(),
|
|
183
260
|
message.session_id,
|
|
184
|
-
options?.initSession ? null : cachedSessionId ?? null
|
|
185
|
-
|
|
261
|
+
options?.initSession ? null : cachedSessionId ?? null,
|
|
262
|
+
] as const;
|
|
263
|
+
if (options?.onlyMainPin) {
|
|
264
|
+
device.updateInternalState(...internalStateArgs, 'standard');
|
|
265
|
+
} else {
|
|
266
|
+
device.updateInternalState(...internalStateArgs);
|
|
267
|
+
}
|
|
186
268
|
|
|
187
269
|
return {
|
|
188
270
|
passphraseState: message.btc_test_address,
|
package/src/types/device.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EDeviceType, type EFirmwareType } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, type EFirmwareType, type HardwareConnectProtocol } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import type { IVersionArray } from './settings';
|
|
4
4
|
import type { PROTO } from '../constants';
|
|
@@ -28,6 +28,8 @@ export type UnavailableCapabilities = { [key: string]: UnavailableCapability };
|
|
|
28
28
|
|
|
29
29
|
export type KnownDevice = {
|
|
30
30
|
connectId: string | null;
|
|
31
|
+
/** Protocol family confirmed by the transport's active probe. */
|
|
32
|
+
connectProtocol?: HardwareConnectProtocol;
|
|
31
33
|
/**
|
|
32
34
|
* Stable physical-device identity. Current SDK responses always include this
|
|
33
35
|
* value; it remains optional in the type during the uuid compatibility window.
|
|
@@ -46,7 +48,7 @@ export type KnownDevice = {
|
|
|
46
48
|
/** Current SDK/transport usage state for connection indicators. */
|
|
47
49
|
status?: DeviceStatus;
|
|
48
50
|
mode: EOneKeyDeviceMode;
|
|
49
|
-
features
|
|
51
|
+
features: Features;
|
|
50
52
|
/** Unified SDK device-state snapshot; features remains a legacy projection. */
|
|
51
53
|
state?: DeviceState;
|
|
52
54
|
unavailableCapabilities: UnavailableCapabilities;
|
|
@@ -61,6 +63,8 @@ export type KnownDevice = {
|
|
|
61
63
|
|
|
62
64
|
export type SearchDevice = {
|
|
63
65
|
connectId: string | null;
|
|
66
|
+
/** Protocol family confirmed by the transport's active probe. */
|
|
67
|
+
connectProtocol?: HardwareConnectProtocol;
|
|
64
68
|
/**
|
|
65
69
|
* Available after device initialization. BLE discovery does not connect to the
|
|
66
70
|
* device, so it returns null until a later initialized device response. Current
|
package/src/types/params.ts
CHANGED
|
@@ -60,7 +60,8 @@ export interface CommonParams {
|
|
|
60
60
|
usePreInitialize?: boolean;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Strictly expected transport protocol. The SDK actively verifies this value and
|
|
64
|
+
* rejects a mismatch. If omitted, a cached protocol only influences probe order.
|
|
64
65
|
*/
|
|
65
66
|
connectProtocol?: HardwareConnectProtocol;
|
|
66
67
|
}
|
|
@@ -122,11 +122,18 @@ export const resolveDeviceBleName = (features?: DeviceFeaturesInput): string | n
|
|
|
122
122
|
export const resolveDeviceFirmwareVersion = (features?: DeviceFeaturesInput): string | null => {
|
|
123
123
|
if (!features) return null;
|
|
124
124
|
const compatible = asCompatibleFeatures(features);
|
|
125
|
+
const bootloaderMode = compatible.bootloaderMode ?? compatible.bootloader_mode;
|
|
125
126
|
return firstMeaningfulVersion(
|
|
126
127
|
compatible.firmwareVersion,
|
|
127
128
|
compatible.onekey_firmware_version,
|
|
128
129
|
compatible.onekey_version,
|
|
129
|
-
|
|
130
|
+
bootloaderMode
|
|
131
|
+
? null
|
|
132
|
+
: versionFromParts(
|
|
133
|
+
compatible.major_version,
|
|
134
|
+
compatible.minor_version,
|
|
135
|
+
compatible.patch_version
|
|
136
|
+
)
|
|
130
137
|
);
|
|
131
138
|
};
|
|
132
139
|
|
|
@@ -85,15 +85,9 @@ export function encodePro2Wallpaper(options: {
|
|
|
85
85
|
for (let x = 0; x < width; x += 1) {
|
|
86
86
|
const sourceOffset = (y * width + x) * 4;
|
|
87
87
|
const thresholdIndex = ((y & 7) << 3) + (x & 7);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (!hasTransparency) {
|
|
92
|
-
const alpha = rgba[sourceOffset + 3];
|
|
93
|
-
red = (red * alpha) >> 8;
|
|
94
|
-
green = (green * alpha) >> 8;
|
|
95
|
-
blue = (blue * alpha) >> 8;
|
|
96
|
-
}
|
|
88
|
+
const red = Math.min(rgba[sourceOffset] + RED_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
|
|
89
|
+
const green = Math.min(rgba[sourceOffset + 1] + GREEN_THRESHOLD[thresholdIndex], 0xff) & 0xfc;
|
|
90
|
+
const blue = Math.min(rgba[sourceOffset + 2] + BLUE_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
|
|
97
91
|
const rgb565 = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
|
|
98
92
|
const rgbOffset = 12 + y * stride + x * 2;
|
|
99
93
|
data[rgbOffset] = rgb565 & 0xff;
|
package/tsconfig.type-tests.json
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"noEmit": true
|
|
5
5
|
},
|
|
6
|
-
"include": [
|
|
6
|
+
"include": [
|
|
7
|
+
"./src",
|
|
8
|
+
"./__tests__/kaspa-public-types.type-test.ts",
|
|
9
|
+
"./__tests__/known-device-public-types.type-test.ts"
|
|
10
|
+
],
|
|
7
11
|
"exclude": ["./dist", "../../node_modules"]
|
|
8
12
|
}
|