@onekeyfe/hd-core 1.2.0-alpha.67 → 1.2.0-alpha.68

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.
Files changed (254) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +313 -0
  2. package/__tests__/DeviceCommands.test.ts +59 -13
  3. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +198 -3
  5. package/__tests__/core-initialization.test.ts +23 -0
  6. package/__tests__/device-connector-protocol.test.ts +81 -0
  7. package/__tests__/device-lifecycle-events.test.ts +105 -3
  8. package/__tests__/device-pool-state.test.ts +22 -0
  9. package/__tests__/device-settings.test.ts +39 -18
  10. package/__tests__/device-state-contract.test.ts +1 -0
  11. package/__tests__/device-state-mapper.test.ts +13 -1
  12. package/__tests__/device-utils.test.ts +48 -1
  13. package/__tests__/deviceSettings.test.ts +11 -1
  14. package/__tests__/deviceUploadNft.test.ts +316 -0
  15. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +5 -0
  16. package/__tests__/get-device-state.test.ts +132 -12
  17. package/__tests__/homescreen.test.ts +41 -1
  18. package/__tests__/kaspa-use-tweak-pro2.test.ts +2 -2
  19. package/__tests__/kaspaSignTransaction.test.ts +3 -26
  20. package/__tests__/logBlockEvent.test.ts +1 -1
  21. package/__tests__/method-protocol-support.test.ts +19 -0
  22. package/__tests__/open-wallet-session.test.ts +568 -37
  23. package/__tests__/pro2Nft.test.ts +108 -0
  24. package/__tests__/protocol-binding.test.ts +89 -0
  25. package/__tests__/protocol-v2-resources.test.ts +340 -0
  26. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  27. package/__tests__/protocol-v2-unlock-policy.test.ts +300 -37
  28. package/__tests__/protocol-v2.test.ts +1449 -867
  29. package/__tests__/protocolV2FileWrite.test.ts +152 -0
  30. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  31. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  32. package/__tests__/search-devices.test.ts +12 -3
  33. package/__tests__/tron-sign-message-init.test.ts +2 -2
  34. package/__tests__/uiProgressThrottle.test.ts +74 -0
  35. package/__tests__/uiPromiseRegistry.test.ts +115 -0
  36. package/dist/api/BaseMethod.d.ts +3 -1
  37. package/dist/api/BaseMethod.d.ts.map +1 -1
  38. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
  39. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  40. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  41. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  42. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  43. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  44. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  45. package/dist/api/FirmwareUpdateV4.d.ts +11 -4
  46. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  47. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  48. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  49. package/dist/api/SearchDevices.d.ts.map +1 -1
  50. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  51. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  52. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  53. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  54. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +4 -3
  55. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  56. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  57. package/dist/api/helpers/protocolV2FileWrite.d.ts +9 -0
  58. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  59. package/dist/api/index.d.ts +2 -0
  60. package/dist/api/index.d.ts.map +1 -1
  61. package/dist/api/kaspa/KaspaGetAddress.d.ts +1 -1
  62. package/dist/api/kaspa/KaspaSignTransaction.d.ts +1 -1
  63. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  64. package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -1
  65. package/dist/api/polkadot/networks.d.ts +1 -1
  66. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  67. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  68. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  69. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  70. package/dist/api/solana/SolSignMessage.d.ts +1 -1
  71. package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -1
  72. package/dist/api/solana/SolSignTransaction.d.ts +2 -2
  73. package/dist/api/sui/SuiGetAddress.d.ts +1 -1
  74. package/dist/api/sui/SuiGetPublicKey.d.ts +1 -1
  75. package/dist/api/sui/SuiSignMessage.d.ts +1 -1
  76. package/dist/api/sui/SuiSignTransaction.d.ts +1 -1
  77. package/dist/api/ton/TonGetAddress.d.ts +1 -1
  78. package/dist/api/ton/TonSignMessage.d.ts +1 -1
  79. package/dist/api/ton/TonSignProof.d.ts +1 -1
  80. package/dist/api/tron/TronSignMessage.d.ts +2 -2
  81. package/dist/api/tron/TronSignTransaction.d.ts +1 -1
  82. package/dist/core/deviceEventRegistration.d.ts +2 -0
  83. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  84. package/dist/core/index.d.ts.map +1 -1
  85. package/dist/core/uiPromiseRegistry.d.ts +6 -0
  86. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  87. package/dist/data-manager/DataManager.d.ts +9 -3
  88. package/dist/data-manager/DataManager.d.ts.map +1 -1
  89. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  90. package/dist/device/Device.d.ts +29 -5
  91. package/dist/device/Device.d.ts.map +1 -1
  92. package/dist/device/DeviceCommands.d.ts +4 -4
  93. package/dist/device/DeviceCommands.d.ts.map +1 -1
  94. package/dist/device/DeviceConnector.d.ts +2 -1
  95. package/dist/device/DeviceConnector.d.ts.map +1 -1
  96. package/dist/device/DevicePool.d.ts.map +1 -1
  97. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -1
  98. package/dist/events/device.d.ts +4 -0
  99. package/dist/events/device.d.ts.map +1 -1
  100. package/dist/events/logBlockEvent.d.ts.map +1 -1
  101. package/dist/events/ui-promise.d.ts +4 -2
  102. package/dist/events/ui-promise.d.ts.map +1 -1
  103. package/dist/events/ui-request.d.ts +31 -2
  104. package/dist/events/ui-request.d.ts.map +1 -1
  105. package/dist/events/ui-response.d.ts +10 -2
  106. package/dist/events/ui-response.d.ts.map +1 -1
  107. package/dist/index.d.ts +243 -69
  108. package/dist/index.js +2563 -1217
  109. package/dist/inject.d.ts +6 -1
  110. package/dist/inject.d.ts.map +1 -1
  111. package/dist/lowLevelInject.d.ts.map +1 -1
  112. package/dist/protocols/protocol-v2/features.d.ts +0 -4
  113. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  114. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  115. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  116. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  117. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  118. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  119. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  120. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  121. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  122. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  123. package/dist/protocols/protocol-v2/walletSession.d.ts +6 -1
  124. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  125. package/dist/topLevelInject.d.ts.map +1 -1
  126. package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -1
  127. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  128. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  129. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  130. package/dist/types/api/deviceUnlock.d.ts +5 -1
  131. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  132. package/dist/types/api/export.d.ts +1 -0
  133. package/dist/types/api/export.d.ts.map +1 -1
  134. package/dist/types/api/firmwareUpdate.d.ts +4 -2
  135. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  136. package/dist/types/api/index.d.ts +6 -1
  137. package/dist/types/api/index.d.ts.map +1 -1
  138. package/dist/types/api/protocolV2.d.ts +3 -0
  139. package/dist/types/api/protocolV2.d.ts.map +1 -1
  140. package/dist/types/device.d.ts +2 -2
  141. package/dist/types/device.d.ts.map +1 -1
  142. package/dist/types/params.d.ts +1 -0
  143. package/dist/types/params.d.ts.map +1 -1
  144. package/dist/types/settings.d.ts +38 -8
  145. package/dist/types/settings.d.ts.map +1 -1
  146. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  147. package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
  148. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  149. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  150. package/dist/utils/deviceSettings.d.ts.map +1 -1
  151. package/dist/utils/homescreen.d.ts +4 -0
  152. package/dist/utils/homescreen.d.ts.map +1 -1
  153. package/dist/utils/index.d.ts +1 -1
  154. package/dist/utils/index.d.ts.map +1 -1
  155. package/dist/utils/patch.d.ts +1 -1
  156. package/dist/utils/patch.d.ts.map +1 -1
  157. package/dist/utils/pro2Nft.d.ts +31 -0
  158. package/dist/utils/pro2Nft.d.ts.map +1 -0
  159. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  160. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  161. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  162. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  163. package/package.json +4 -4
  164. package/src/api/BaseMethod.ts +8 -10
  165. package/src/api/CheckAllFirmwareRelease.ts +61 -11
  166. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  167. package/src/api/FirmwareUpdate.ts +6 -2
  168. package/src/api/FirmwareUpdateV2.ts +5 -2
  169. package/src/api/FirmwareUpdateV3.ts +6 -2
  170. package/src/api/FirmwareUpdateV4.ts +463 -258
  171. package/src/api/GetPassphraseState.ts +7 -1
  172. package/src/api/OpenWalletSession.ts +71 -17
  173. package/src/api/SearchDevices.ts +3 -1
  174. package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
  175. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +36 -11
  176. package/src/api/device/DeviceSettings.ts +5 -4
  177. package/src/api/device/DeviceUnlock.ts +8 -3
  178. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -12
  179. package/src/api/firmware/uploadFirmware.ts +17 -4
  180. package/src/api/helpers/protocolV2FileWrite.ts +202 -63
  181. package/src/api/index.ts +2 -0
  182. package/src/api/kaspa/KaspaGetAddress.ts +1 -1
  183. package/src/api/kaspa/KaspaSignTransaction.ts +2 -13
  184. package/src/api/polkadot/networks.ts +3 -3
  185. package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
  186. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  187. package/src/api/protocol-v2/helpers.ts +1 -1
  188. package/src/api/solana/SolSignMessage.ts +1 -1
  189. package/src/api/solana/SolSignOffchainMessage.ts +1 -1
  190. package/src/api/solana/SolSignTransaction.ts +2 -2
  191. package/src/api/sui/SuiGetAddress.ts +1 -1
  192. package/src/api/sui/SuiGetPublicKey.ts +1 -1
  193. package/src/api/sui/SuiSignMessage.ts +1 -1
  194. package/src/api/sui/SuiSignTransaction.ts +1 -1
  195. package/src/api/ton/TonGetAddress.ts +1 -1
  196. package/src/api/ton/TonSignMessage.ts +1 -1
  197. package/src/api/ton/TonSignProof.ts +1 -1
  198. package/src/api/tron/TronSignMessage.ts +2 -2
  199. package/src/api/tron/TronSignTransaction.ts +1 -1
  200. package/src/core/deviceEventRegistration.ts +4 -0
  201. package/src/core/index.ts +213 -111
  202. package/src/core/uiPromiseRegistry.ts +63 -0
  203. package/src/data/messages/messages-protocol-v2.json +25 -0
  204. package/src/data-manager/DataManager.ts +88 -11
  205. package/src/data-manager/TransportManager.ts +6 -0
  206. package/src/device/Device.ts +273 -43
  207. package/src/device/DeviceCommands.ts +31 -4
  208. package/src/device/DeviceConnector.ts +33 -13
  209. package/src/device/DevicePool.ts +21 -7
  210. package/src/deviceProfile/buildDeviceFeatures.ts +2 -2
  211. package/src/deviceProfile/protocolV2DeviceIdentity.ts +3 -0
  212. package/src/events/device.ts +4 -0
  213. package/src/events/logBlockEvent.ts +1 -0
  214. package/src/events/ui-promise.ts +4 -2
  215. package/src/events/ui-request.ts +36 -2
  216. package/src/events/ui-response.ts +12 -2
  217. package/src/inject.ts +58 -2
  218. package/src/lowLevelInject.ts +6 -3
  219. package/src/protocols/protocol-v2/features.ts +10 -7
  220. package/src/protocols/protocol-v2/firmware.ts +2 -0
  221. package/src/protocols/protocol-v2/resources.ts +390 -0
  222. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  223. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  224. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
  225. package/src/protocols/protocol-v2/walletSession.ts +214 -36
  226. package/src/topLevelInject.ts +6 -3
  227. package/src/types/api/checkAllFirmwareRelease.ts +3 -1
  228. package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
  229. package/src/types/api/deviceUnlock.ts +12 -1
  230. package/src/types/api/export.ts +1 -0
  231. package/src/types/api/firmwareUpdate.ts +6 -3
  232. package/src/types/api/index.ts +13 -0
  233. package/src/types/api/protocolV2.ts +13 -0
  234. package/src/types/device.ts +15 -3
  235. package/src/types/params.ts +7 -1
  236. package/src/types/settings.ts +60 -9
  237. package/src/utils/deviceFeaturesCompat.ts +5 -0
  238. package/src/utils/deviceFeaturesUtils.ts +14 -3
  239. package/src/utils/deviceInfoUtils.ts +2 -0
  240. package/src/utils/deviceSettings.ts +3 -0
  241. package/src/utils/homescreen.ts +32 -6
  242. package/src/utils/index.ts +6 -1
  243. package/src/utils/pro2Nft.ts +142 -0
  244. package/src/utils/pro2Wallpaper.ts +35 -8
  245. package/src/utils/uiProgressThrottle.ts +63 -0
  246. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  247. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  248. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  249. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  250. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  251. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  252. package/src/api/firmware/progressThrottle.ts +0 -44
  253. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  254. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
@@ -0,0 +1,131 @@
1
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
3
+
4
+ import { LoggerNames, getLogger } from '../../utils';
5
+ import { isProtocolV2UiEnabled, resolveProtocolV2UiInteraction } from './uiInteraction';
6
+ import { refreshProtocolV2DeviceStatus } from './walletSession';
7
+
8
+ import type { BaseMethod } from '../../api/BaseMethod';
9
+ import type { Device } from '../../device/Device';
10
+ import type { HardwareUiInteractionMeta } from '../../events';
11
+ import type { ProtocolV2UiInteractionCoordinator } from './uiInteraction';
12
+
13
+ const Log = getLogger(LoggerNames.Core);
14
+
15
+ export type ProtocolV2UnlockContext = {
16
+ preflightCompleted: boolean;
17
+ };
18
+
19
+ export const createProtocolV2UnlockContext = (): ProtocolV2UnlockContext => ({
20
+ preflightCompleted: false,
21
+ });
22
+
23
+ type RunnableMethod = Pick<
24
+ BaseMethod,
25
+ | 'run'
26
+ | 'unlockPolicy'
27
+ | 'useDevicePassphraseState'
28
+ | 'protocolV2UiInteraction'
29
+ | 'protocolV2UiMode'
30
+ | 'params'
31
+ | 'payload'
32
+ > & {
33
+ name?: string;
34
+ protocolV2UnlockContext?: ProtocolV2UnlockContext;
35
+ };
36
+
37
+ type UiInteractionCoordinator = Pick<
38
+ ProtocolV2UiInteractionCoordinator,
39
+ 'enterMethodInteraction' | 'enterUnlockInteraction'
40
+ >;
41
+
42
+ type RunMethodWithUnlockPolicyOptions<T> = {
43
+ context?: ProtocolV2UnlockContext;
44
+ uiCoordinator?: UiInteractionCoordinator;
45
+ afterStatusBeforeUnlock?: () => void | Promise<void>;
46
+ prepare?: () => Promise<void>;
47
+ run?: () => Promise<T>;
48
+ };
49
+
50
+ const resolvePreUnlockPinType = (method: RunnableMethod) => {
51
+ const targetsKnownHiddenWallet =
52
+ method.useDevicePassphraseState &&
53
+ method.payload?.useEmptyPassphrase !== true &&
54
+ typeof method.payload?.passphraseState === 'string' &&
55
+ method.payload.passphraseState.length > 0;
56
+
57
+ // A hidden-wallet call may be unlocked by either its Attach PIN or the main
58
+ // PIN followed by passphrase selection. Wallet-session verification still
59
+ // rejects a different hidden wallet after the device is unlocked.
60
+ return targetsKnownHiddenWallet ? DeviceSessionPinType.Any : DeviceSessionPinType.Main;
61
+ };
62
+
63
+ export async function runMethodWithUnlockPolicy<T = unknown>(
64
+ method: RunnableMethod,
65
+ device: Device,
66
+ options: RunMethodWithUnlockPolicyOptions<T> = {}
67
+ ): Promise<T> {
68
+ const {
69
+ context = createProtocolV2UnlockContext(),
70
+ uiCoordinator,
71
+ afterStatusBeforeUnlock,
72
+ prepare,
73
+ run: configuredRun,
74
+ } = options;
75
+ const run = configuredRun ?? (() => method.run() as Promise<T>);
76
+ method.protocolV2UnlockContext = context;
77
+
78
+ const shouldEmitUi = isProtocolV2UiEnabled(method);
79
+ const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
80
+ const requiresPreUnlock =
81
+ device.isProtocolV2() &&
82
+ (method.useDevicePassphraseState || method.unlockPolicy === 'unlock-before-run') &&
83
+ !device.isBootloader?.() &&
84
+ !device.isRomloader?.();
85
+
86
+ if (requiresPreUnlock && !context.preflightCompleted) {
87
+ const status = await refreshProtocolV2DeviceStatus(device);
88
+ if (typeof status?.unlocked !== 'boolean') {
89
+ throw ERRORS.TypedError(
90
+ HardwareErrorCode.RuntimeError,
91
+ 'Protocol V2 DeviceStatus did not report an explicit unlock state.'
92
+ );
93
+ }
94
+
95
+ await afterStatusBeforeUnlock?.();
96
+
97
+ if (!status.unlocked) {
98
+ const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
99
+ ? uiCoordinator.enterUnlockInteraction(method.name)
100
+ : undefined;
101
+ const unlockedStatus = await device.unlockDevice(
102
+ resolvePreUnlockPinType(method),
103
+ shouldCoordinateUi
104
+ ? { emitUiEvent: false, interaction: unlockInteraction }
105
+ : {
106
+ source: 'unlock-coordinator',
107
+ reason: 'device-locked',
108
+ deviceOnly: true,
109
+ method: method.name,
110
+ }
111
+ );
112
+ if (unlockedStatus?.unlocked !== true) {
113
+ throw ERRORS.TypedError(
114
+ HardwareErrorCode.RuntimeError,
115
+ 'Protocol V2 device remained locked after the unlock flow.'
116
+ );
117
+ }
118
+ Log.debug('Protocol V2 pre-unlock completed', { method: method.name });
119
+ }
120
+
121
+ context.preflightCompleted = true;
122
+ }
123
+
124
+ await prepare?.();
125
+
126
+ if (shouldCoordinateUi) {
127
+ uiCoordinator.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
128
+ }
129
+
130
+ return run();
131
+ }
@@ -1,5 +1,5 @@
1
1
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
- import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
2
+ import { DeviceSessionPinType, DeviceSessionSeedDomain } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import { DEVICE } from '../../events';
5
5
  import { assertCompleteDeviceSession } from './deviceSession';
@@ -38,32 +38,74 @@ export async function refreshProtocolV2DeviceStatus(device: Device) {
38
38
  return device.updateProtocolV2Status(status);
39
39
  }
40
40
 
41
- const negotiateEventlessWalletSession = async (device: Device) => {
42
- await device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
43
- eventless_wallet_session: true,
41
+ export async function ensureProtocolV2WalletSessionUnlocked(device: Device) {
42
+ if (!device.isProtocolV2() || device.isBootloader?.() || device.isRomloader?.()) {
43
+ return false;
44
+ }
45
+
46
+ await refreshProtocolV2DeviceStatus(device);
47
+ if (device.state?.status.unlocked !== false) {
48
+ return false;
49
+ }
50
+
51
+ await device.unlockDevice(DeviceSessionPinType.Main, {
52
+ source: 'unlock-coordinator',
53
+ reason: 'device-locked',
54
+ deviceOnly: true,
44
55
  });
56
+ return true;
57
+ }
58
+
59
+ const negotiateEventlessWalletSession = async (device: Device) => {
60
+ await device.ensureProtocolV2RuntimeContext();
45
61
  };
46
62
 
47
63
  const getDeviceSession = async (device: Device, request: DeviceSessionGet) =>
48
64
  device.commands.typedCall('DeviceSessionGet', 'DeviceSession', request);
49
65
 
66
+ const buildDeviceSessionGetRequest = ({
67
+ sessionId,
68
+ expectedPassphraseState,
69
+ deriveCardano,
70
+ }: {
71
+ sessionId?: string;
72
+ expectedPassphraseState?: string;
73
+ deriveCardano?: boolean;
74
+ } = {}): DeviceSessionGet => ({
75
+ ...(sessionId ? { session_id: sessionId } : {}),
76
+ ...(expectedPassphraseState ? { btc_test_address: expectedPassphraseState } : {}),
77
+ seed_domains:
78
+ deriveCardano === undefined
79
+ ? []
80
+ : [
81
+ DeviceSessionSeedDomain.SeedDomain_Standard,
82
+ ...(deriveCardano ? [DeviceSessionSeedDomain.SeedDomain_Cardano] : []),
83
+ ],
84
+ });
85
+
50
86
  const askDevicePassphrase = async (device: Device, requestPayload: DeviceSessionAskPassphrase) => {
51
87
  await device.commands.typedCall('DeviceSessionAskPassphrase', 'Success', requestPayload);
52
88
  await refreshProtocolV2DeviceStatus(device);
53
89
  };
54
90
 
55
- const selectDeviceSession = async (device: Device, expectedPassphraseState?: string) => {
91
+ const selectDeviceSession = async (
92
+ device: Device,
93
+ expectedPassphraseState?: string,
94
+ deriveCardano?: boolean
95
+ ) => {
56
96
  const existsAttachPinUser = device.features?.attachToPinEnabled === true;
57
97
  const metadata = {
58
98
  source: 'wallet-session-coordinator' as const,
59
99
  reason: expectedPassphraseState ? ('session-recovery' as const) : ('open-wallet' as const),
60
100
  ...(expectedPassphraseState ? { expectedPassphraseState } : {}),
61
101
  };
102
+ const passphraseInteraction = device.createProtocolV2UiPhaseMetadata?.('passphrase', 'start');
62
103
  const response = await device.commands.promptPassphrase(
63
104
  {
64
105
  existsAttachPinUser,
65
106
  deviceOnly: false,
66
107
  ...metadata,
108
+ ...(passphraseInteraction ? { interaction: passphraseInteraction } : {}),
67
109
  },
68
110
  { cancelDeviceOnReject: false }
69
111
  );
@@ -102,9 +144,16 @@ const selectDeviceSession = async (device: Device, expectedPassphraseState?: str
102
144
  'Attach PIN wallet selection is unavailable on this device.'
103
145
  );
104
146
  }
105
- device.emit(DEVICE.ATTACH_PIN_ON_DEVICE, device, metadata);
106
- await device.unlockDevice(DeviceSessionPinType.AttachToPin);
107
- return getDeviceSession(device, {});
147
+ const attachPinInteraction = device.createProtocolV2UiPhaseMetadata?.('pin', 'start');
148
+ device.emit(DEVICE.ATTACH_PIN_ON_DEVICE, device, {
149
+ ...metadata,
150
+ ...(attachPinInteraction ? { interaction: attachPinInteraction } : {}),
151
+ });
152
+ await device.unlockDevice(DeviceSessionPinType.AttachToPin, {
153
+ emitUiEvent: false,
154
+ interaction: attachPinInteraction,
155
+ });
156
+ return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
108
157
  }
109
158
 
110
159
  if (hasHostPassphrase) {
@@ -112,12 +161,19 @@ const selectDeviceSession = async (device: Device, expectedPassphraseState?: str
112
161
  passphrase: hostPassphrase,
113
162
  on_device: false,
114
163
  });
115
- return getDeviceSession(device, {});
164
+ return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
116
165
  }
117
166
 
118
- device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, metadata);
167
+ const passphraseOnDeviceInteraction = device.createProtocolV2UiPhaseMetadata?.(
168
+ 'passphrase-on-device',
169
+ 'start'
170
+ );
171
+ device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, {
172
+ ...metadata,
173
+ ...(passphraseOnDeviceInteraction ? { interaction: passphraseOnDeviceInteraction } : {}),
174
+ });
119
175
  await askDevicePassphrase(device, { on_device: true });
120
- return getDeviceSession(device, {});
176
+ return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
121
177
  };
122
178
 
123
179
  export async function getProtocolV2WalletSession(
@@ -128,11 +184,18 @@ export async function getProtocolV2WalletSession(
128
184
  initSession?: boolean;
129
185
  expectedPassphraseState?: string;
130
186
  onlyMainPin?: boolean;
187
+ /** 业务安全校验遇到 Attach PIN 上下文时直接拒绝,不主动切换钱包。 */
188
+ rejectAttachPinForMainWallet?: boolean;
189
+ selectMainWalletBeforeRestore?: boolean;
131
190
  resumeOnly?: boolean;
191
+ deriveCardano?: boolean;
192
+ /** Read the wallet already selected by Attach PIN without reopening wallet selection. */
193
+ readCurrentAttachPinSession?: boolean;
132
194
  }
133
195
  ) {
134
196
  const forceWalletSelection =
135
197
  options?.forceWalletSelection === true || options?.initSession === true;
198
+ const readCurrentAttachPinSession = options?.readCurrentAttachPinSession === true;
136
199
 
137
200
  if (forceWalletSelection) {
138
201
  if (options.onlyMainPin) {
@@ -145,33 +208,114 @@ export async function getProtocolV2WalletSession(
145
208
 
146
209
  await negotiateEventlessWalletSession(device);
147
210
 
148
- if (options?.onlyMainPin) {
211
+ if (options?.onlyMainPin || readCurrentAttachPinSession) {
149
212
  device.passphraseState = undefined;
150
213
  }
151
- let expectedPassphraseState = options?.onlyMainPin
152
- ? undefined
153
- : options?.expectedPassphraseState ?? device.passphraseState;
214
+ let expectedPassphraseState =
215
+ options?.onlyMainPin || readCurrentAttachPinSession
216
+ ? undefined
217
+ : options?.expectedPassphraseState ?? device.passphraseState;
154
218
 
155
219
  const cachedStandardSession = options?.onlyMainPin
156
220
  ? device.getStandardInternalState?.()
157
221
  : undefined;
158
222
  const cachedSessionId =
159
- !options?.onlyMainPin && typeof device.getInternalState === 'function'
223
+ !options?.onlyMainPin &&
224
+ !readCurrentAttachPinSession &&
225
+ typeof device.getInternalState === 'function'
160
226
  ? device.getInternalState()
161
227
  : undefined;
162
228
  let response;
163
229
  let resumed = false;
230
+ let mainPinSelected =
231
+ options?.onlyMainPin === true &&
232
+ device.features?.unlocked === true &&
233
+ device.features?.unlockedAttachPin === false;
164
234
 
165
- if (options?.onlyMainPin) {
235
+ const clearCurrentWalletSession = () => {
236
+ if (options?.onlyMainPin) {
237
+ device.clearStandardInternalState?.();
238
+ } else {
239
+ device.clearInternalState();
240
+ }
241
+ };
242
+
243
+ const rejectMismatchedAttachPinWallet = async () => {
244
+ const features = await refreshProtocolV2DeviceStatus(device);
245
+ if (features.unlockedAttachPin !== true) {
246
+ return;
247
+ }
248
+
249
+ try {
250
+ await device.lockDevice();
251
+ } catch {
252
+ // Reject the mismatched Attach PIN wallet even when older firmware cannot lock.
253
+ }
254
+ if (options?.rejectAttachPinForMainWallet) {
255
+ device.clearStandardInternalState?.();
256
+ device.clearInternalState();
257
+ } else {
258
+ clearCurrentWalletSession();
259
+ }
260
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckUnlockTypeError);
261
+ };
262
+
263
+ if (options?.onlyMainPin && options.rejectAttachPinForMainWallet) {
264
+ await rejectMismatchedAttachPinWallet();
265
+ }
266
+
267
+ const selectMainPin = async (force = false) => {
268
+ if (force || !mainPinSelected) {
269
+ await device.unlockDevice(DeviceSessionPinType.Main, {
270
+ source: 'wallet-session-coordinator',
271
+ reason: expectedPassphraseState ? 'session-recovery' : 'open-wallet',
272
+ deviceOnly: true,
273
+ });
274
+ mainPinSelected = true;
275
+ }
276
+ };
277
+
278
+ const selectStandardWallet = async () => {
279
+ // Main PIN authenticates the device; an empty host passphrase selects the standard derivation.
280
+ await selectMainPin();
281
+ if (device.features?.passphraseProtection === true) {
282
+ await askDevicePassphrase(device, {
283
+ passphrase: '',
284
+ on_device: false,
285
+ });
286
+ }
287
+ };
288
+
289
+ if (readCurrentAttachPinSession) {
290
+ const status = await requestProtocolV2DeviceStatus(device);
291
+ device.updateProtocolV2Status(status);
292
+ if (
293
+ status.unlocked !== true ||
294
+ status.passphrase_enabled !== true ||
295
+ status.unlocked_by_attach_to_pin !== true
296
+ ) {
297
+ device.clearInternalState();
298
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckUnlockTypeError);
299
+ }
300
+ response = await getDeviceSession(
301
+ device,
302
+ buildDeviceSessionGetRequest({ deriveCardano: options?.deriveCardano })
303
+ );
304
+ } else if (options?.onlyMainPin) {
166
305
  expectedPassphraseState = cachedStandardSession?.passphraseState;
167
306
  if (cachedStandardSession) {
168
307
  try {
169
- if (device.features?.unlockedAttachPin === true) {
170
- await device.unlockDevice(DeviceSessionPinType.Main);
308
+ if (options?.selectMainWalletBeforeRestore) {
309
+ await selectMainPin();
171
310
  }
172
- response = await getDeviceSession(device, {
173
- session_id: cachedStandardSession.sessionId,
174
- });
311
+ response = await getDeviceSession(
312
+ device,
313
+ buildDeviceSessionGetRequest({
314
+ sessionId: cachedStandardSession.sessionId,
315
+ expectedPassphraseState,
316
+ deriveCardano: options?.deriveCardano,
317
+ })
318
+ );
175
319
  resumed = true;
176
320
  } catch (error) {
177
321
  device.clearStandardInternalState?.();
@@ -183,12 +327,22 @@ export async function getProtocolV2WalletSession(
183
327
  }
184
328
 
185
329
  if (!response) {
186
- await device.unlockDevice(DeviceSessionPinType.Main);
187
- response = await getDeviceSession(device, {});
330
+ await selectStandardWallet();
331
+ response = await getDeviceSession(
332
+ device,
333
+ buildDeviceSessionGetRequest({ deriveCardano: options?.deriveCardano })
334
+ );
188
335
  }
189
336
  } else if (cachedSessionId && expectedPassphraseState) {
190
337
  try {
191
- response = await getDeviceSession(device, { session_id: cachedSessionId });
338
+ response = await getDeviceSession(
339
+ device,
340
+ buildDeviceSessionGetRequest({
341
+ sessionId: cachedSessionId,
342
+ expectedPassphraseState,
343
+ deriveCardano: options?.deriveCardano,
344
+ })
345
+ );
192
346
  resumed = true;
193
347
  } catch (error) {
194
348
  device.clearInternalState();
@@ -197,6 +351,20 @@ export async function getProtocolV2WalletSession(
197
351
  }
198
352
  resumed = false;
199
353
  }
354
+ } else if (expectedPassphraseState) {
355
+ try {
356
+ response = await getDeviceSession(
357
+ device,
358
+ buildDeviceSessionGetRequest({
359
+ expectedPassphraseState,
360
+ deriveCardano: options?.deriveCardano,
361
+ })
362
+ );
363
+ } catch (error) {
364
+ if (options?.resumeOnly || !isWalletSessionInvalidError(error)) {
365
+ throw error;
366
+ }
367
+ }
200
368
  }
201
369
 
202
370
  if (!response) {
@@ -204,7 +372,7 @@ export async function getProtocolV2WalletSession(
204
372
  device.clearInternalState();
205
373
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
206
374
  }
207
- response = await selectDeviceSession(device, expectedPassphraseState);
375
+ response = await selectDeviceSession(device, expectedPassphraseState, options?.deriveCardano);
208
376
  }
209
377
 
210
378
  let { message } = response;
@@ -220,17 +388,21 @@ export async function getProtocolV2WalletSession(
220
388
  }
221
389
  if (expectedPassphraseState && expectedPassphraseState !== message.btc_test_address) {
222
390
  resumed = false;
391
+ await rejectMismatchedAttachPinWallet();
223
392
  if (options?.resumeOnly) {
224
393
  device.clearInternalState();
225
394
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
226
395
  }
227
396
  if (options?.onlyMainPin) {
228
397
  device.clearStandardInternalState?.();
229
- await device.unlockDevice(DeviceSessionPinType.Main);
230
- response = await getDeviceSession(device, {});
398
+ await selectStandardWallet();
399
+ response = await getDeviceSession(
400
+ device,
401
+ buildDeviceSessionGetRequest({ deriveCardano: options?.deriveCardano })
402
+ );
231
403
  } else {
232
404
  device.clearInternalState();
233
- response = await selectDeviceSession(device, expectedPassphraseState);
405
+ response = await selectDeviceSession(device, expectedPassphraseState, options?.deriveCardano);
234
406
  }
235
407
  message = response.message;
236
408
  try {
@@ -244,11 +416,8 @@ export async function getProtocolV2WalletSession(
244
416
  throw error;
245
417
  }
246
418
  if (expectedPassphraseState !== message.btc_test_address) {
247
- if (options?.onlyMainPin) {
248
- device.clearStandardInternalState?.();
249
- } else {
250
- device.clearInternalState();
251
- }
419
+ await rejectMismatchedAttachPinWallet();
420
+ clearCurrentWalletSession();
252
421
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
253
422
  }
254
423
  }
@@ -266,20 +435,29 @@ export async function getProtocolV2WalletSession(
266
435
  device.updateInternalState(...internalStateArgs);
267
436
  }
268
437
 
438
+ let unlockedAttachPin: boolean | undefined;
439
+ if (readCurrentAttachPinSession) {
440
+ unlockedAttachPin = true;
441
+ } else if (mainPinSelected) {
442
+ unlockedAttachPin = false;
443
+ }
444
+
269
445
  return {
270
446
  passphraseState: message.btc_test_address,
271
447
  newSession: message.session_id,
272
- unlockedAttachPin: device.features?.unlockedAttachPin ?? undefined,
448
+ unlockedAttachPin,
273
449
  resumed,
274
450
  };
275
451
  }
276
452
 
277
453
  export async function restoreProtocolV2WalletSession(
278
454
  device: Device,
279
- expectedPassphraseState: string
455
+ expectedPassphraseState: string,
456
+ deriveCardano?: boolean
280
457
  ) {
281
458
  return getProtocolV2WalletSession(device, {
282
459
  expectedPassphraseState,
283
460
  resumeOnly: true,
461
+ deriveCardano,
284
462
  });
285
463
  }
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
 
3
- import { createCoreApi } from './inject';
3
+ import { createCoreApi, createProtocolAwareCall } from './inject';
4
4
  import { unregisterFirmwareUpdateHostBinding } from './api/firmware/FirmwareHostBinding';
5
5
 
6
6
  import type { ConnectSettings } from './types/settings';
@@ -20,6 +20,7 @@ export const topLevelInject = () => {
20
20
  if (!lowLevelApi) return Promise.resolve(undefined);
21
21
  return lowLevelApi.call(params);
22
22
  };
23
+ const protocolAwareCall = createProtocolAwareCall(call);
23
24
  const api: CoreApi = {
24
25
  on: <T extends string, P extends (...args: any[]) => any>(type: T, fn: P) => {
25
26
  eventEmitter.on(type, fn);
@@ -38,9 +39,11 @@ export const topLevelInject = () => {
38
39
  return lowLevelApi?.init(settings) ?? Promise.resolve(false);
39
40
  },
40
41
 
41
- call,
42
+ call: protocolAwareCall.call,
42
43
 
43
- ...createCoreApi(call),
44
+ setDeviceConnectProtocol: protocolAwareCall.setDeviceConnectProtocol,
45
+
46
+ ...createCoreApi(protocolAwareCall.call),
44
47
 
45
48
  removeAllListeners: type => {
46
49
  eventEmitter.removeAllListeners(type);
@@ -55,11 +55,12 @@ export type AllFirmwareRelease = {
55
55
  bridge?: FirmwareRelease;
56
56
  features?: Features;
57
57
  protocol?: 'V1' | 'V2';
58
- deviceType?: 'pro2';
58
+ deviceType?: 'pro2' | 'neo';
59
59
  firmwareType?: EFirmwareType;
60
60
  status?: ProtocolV2FirmwareReleaseStatus;
61
61
  hasUpgrade?: boolean;
62
62
  required?: boolean;
63
+ resourceStatus?: 'valid' | 'outdated' | 'unknown';
63
64
  currentVersions?: DeviceStateVersions;
64
65
  components?: ProtocolV2FirmwareComponentRelease[];
65
66
  targetsToUpdate?: FirmwareUpdateV4Target[];
@@ -69,6 +70,7 @@ export type AllFirmwareRelease = {
69
70
 
70
71
  export type CheckAllFirmwareReleaseParams = {
71
72
  checkBridgeRelease?: boolean;
73
+ checkFirmwareHash?: boolean;
72
74
  firmwareType?: EFirmwareType;
73
75
  platform?: 'native' | 'desktop' | 'ext' | 'web' | 'web-embed';
74
76
  forceUpdateTargets?: FirmwareUpdatePlanForceTarget[];
@@ -0,0 +1,6 @@
1
+ import type { HardwareConnectProtocol } from '@onekeyfe/hd-shared';
2
+ import type { Response } from '../params';
3
+
4
+ export declare function detectDeviceConnectProtocol(
5
+ connectId: string
6
+ ): Response<HardwareConnectProtocol>;
@@ -1,4 +1,15 @@
1
+ import type { DeviceSessionPinType } from '@onekeyfe/hd-transport';
1
2
  import type { Features } from '../device';
2
3
  import type { CommonParams, Response } from '../params';
3
4
 
4
- export declare function deviceUnlock(connectId: string, params?: CommonParams): Response<Features>;
5
+ export type DeviceUnlockParams = {
6
+ /**
7
+ * 限定本次解锁允许使用的 PIN 类型。省略时保持兼容行为,仅允许主 PIN。
8
+ */
9
+ pinType?: DeviceSessionPinType;
10
+ };
11
+
12
+ export declare function deviceUnlock(
13
+ connectId: string,
14
+ params?: CommonParams & DeviceUnlockParams
15
+ ): Response<Features>;
@@ -14,6 +14,7 @@ export type {
14
14
  export type { CipheredKeyValue, CipheredKeyValueParams } from './cipherKeyValue';
15
15
 
16
16
  export type { DeviceChangePinParams } from './deviceChangePin';
17
+ export type { DeviceUnlockParams } from './deviceUnlock';
17
18
  export type { DeviceFlagsParams } from './deviceFlags';
18
19
  export type { DeviceRecoveryParams } from './deviceRecovery';
19
20
  export type { DeviceResetParams } from './deviceReset';
@@ -118,6 +118,7 @@ export interface FirmwareUpdateV3Params {
118
118
  */
119
119
  export type FirmwareUpdateV4Target =
120
120
  | 'boot'
121
+ | 'boot_resources'
121
122
  | 'app_v1'
122
123
  | 'app_v2'
123
124
  | 'coprocessor'
@@ -154,12 +155,14 @@ export interface FirmwareUpdateV4Params {
154
155
  se04Binary?: ArrayBuffer;
155
156
  forcedUpdateRes?: boolean;
156
157
  /**
157
- * RESC bundle okpkg files written directly to devicePath through FilesystemFileWrite.
158
- * Manual mode installs them without version comparison.
158
+ * Arbitrary Protocol V2 resource files written directly with FilesystemFileWrite.
159
+ * Use this for manifest-driven boot resources and other non-RESC files.
159
160
  */
160
- resourceBundleFiles?: Array<{
161
+ resourceFiles?: Array<{
161
162
  binary: ArrayBuffer;
162
163
  devicePath: string;
164
+ size?: number;
165
+ fileHash?: string;
163
166
  }>;
164
167
  artifactReader?: FirmwareArtifactReader;
165
168
  componentArtifacts?: Partial<