@onekeyfe/hd-core 1.2.0-alpha.41 → 1.2.0-alpha.42
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 +187 -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 +259 -18
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-v2-resources.test.ts +204 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +56 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +86 -35
- package/__tests__/protocol-v2.test.ts +526 -955
- 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 +30 -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 +22 -4
- package/dist/device/Device.d.ts.map +1 -1
- 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 +27 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +110 -182
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1919 -3336
- 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 +3 -3
- 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 +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 +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 +166 -0
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +118 -75
- package/src/data/messages/messages-protocol-v2.json +53 -0
- package/src/data-manager/DataManager.ts +63 -38
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +193 -21
- package/src/events/device.ts +4 -0
- package/src/events/logBlockEvent.ts +1 -0
- package/src/events/ui-request.ts +32 -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 +31 -5
- 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 +93 -18
- 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 +91 -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
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
IProtocolV2Resource,
|
|
5
|
+
IProtocolV2ResourceType,
|
|
6
|
+
IProtocolV2Resources,
|
|
7
|
+
} from '../../types';
|
|
8
|
+
import type { DeviceCommands } from '../../device/DeviceCommands';
|
|
9
|
+
import type { ResourceInventory } from '@onekeyfe/hd-transport';
|
|
10
|
+
|
|
11
|
+
export const PROTOCOL_V2_RESOURCE_TYPES = [
|
|
12
|
+
'images',
|
|
13
|
+
'animation',
|
|
14
|
+
'wallpaper',
|
|
15
|
+
'translations',
|
|
16
|
+
'roobert',
|
|
17
|
+
'noto',
|
|
18
|
+
] as const satisfies readonly IProtocolV2ResourceType[];
|
|
19
|
+
|
|
20
|
+
export const PROTOCOL_V2_RESOURCE_DEVICE_PATHS: Readonly<Record<IProtocolV2ResourceType, string>> =
|
|
21
|
+
{
|
|
22
|
+
images: 'vol0:/bundles/images/images.okpkg',
|
|
23
|
+
animation: 'vol0:/bundles/images/animation.okpkg',
|
|
24
|
+
wallpaper: 'vol0:/bundles/images/wallpaper.okpkg',
|
|
25
|
+
translations: 'vol0:/bundles/translations/translations.okpkg',
|
|
26
|
+
roobert: 'vol0:/bundles/font/roobert.okpkg',
|
|
27
|
+
noto: 'vol0:/bundles/font/noto.okpkg',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const RESOURCE_TYPE_SET = new Set<string>(PROTOCOL_V2_RESOURCE_TYPES);
|
|
31
|
+
const SHA256_HEX_LENGTH = 64;
|
|
32
|
+
const SHA3_512_HEX_LENGTH = 128;
|
|
33
|
+
export const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
|
|
34
|
+
|
|
35
|
+
const RESOURCE_TYPE_BY_DEVICE_VALUE: Readonly<Record<string, IProtocolV2ResourceType>> = {
|
|
36
|
+
'0': 'images',
|
|
37
|
+
IMAGES: 'images',
|
|
38
|
+
'1': 'animation',
|
|
39
|
+
ANIMATION: 'animation',
|
|
40
|
+
'2': 'wallpaper',
|
|
41
|
+
WALLPAPER: 'wallpaper',
|
|
42
|
+
'3': 'translations',
|
|
43
|
+
TRANSLATIONS: 'translations',
|
|
44
|
+
'4': 'roobert',
|
|
45
|
+
ROOBERT: 'roobert',
|
|
46
|
+
'5': 'noto',
|
|
47
|
+
NOTO: 'noto',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type ProtocolV2ResourceInventoryItem = {
|
|
51
|
+
type: IProtocolV2ResourceType;
|
|
52
|
+
size: number;
|
|
53
|
+
headerHash: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type ProtocolV2ResourceUpdateMode = 'application' | 'bootloader-recovery';
|
|
57
|
+
|
|
58
|
+
export type ProtocolV2ResourceUpdatePlan = {
|
|
59
|
+
status: 'valid' | 'outdated' | 'unknown';
|
|
60
|
+
resources: IProtocolV2Resource[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Normalize the success-only device response into the SDK resource identity shape. */
|
|
64
|
+
export function parseProtocolV2ResourceInventory(
|
|
65
|
+
value: ResourceInventory | unknown
|
|
66
|
+
): ProtocolV2ResourceInventoryItem[] {
|
|
67
|
+
const items = (value as { items?: unknown })?.items;
|
|
68
|
+
if (!Array.isArray(items)) {
|
|
69
|
+
throw new Error('Invalid Pro2 resource inventory: items must be an array');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const inventory = items.map((item, index) => {
|
|
73
|
+
if (!item || typeof item !== 'object') {
|
|
74
|
+
throw new Error(`Invalid Pro2 resource inventory item at ${index}`);
|
|
75
|
+
}
|
|
76
|
+
const raw = item as { type?: unknown; size?: unknown; header_hash?: unknown };
|
|
77
|
+
const type = RESOURCE_TYPE_BY_DEVICE_VALUE[String(raw.type).toUpperCase()];
|
|
78
|
+
if (!type) {
|
|
79
|
+
throw new Error(`Invalid Pro2 resource inventory type at ${index}`);
|
|
80
|
+
}
|
|
81
|
+
if (!Number.isSafeInteger(raw.size) || Number(raw.size) <= 0) {
|
|
82
|
+
throw new Error(`Invalid Pro2 resource inventory size at ${index}`);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
type,
|
|
86
|
+
size: Number(raw.size),
|
|
87
|
+
headerHash: normalizeHex(raw.header_hash, SHA3_512_HEX_LENGTH, 'inventory headerHash'),
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (new Set(inventory.map(item => item.type)).size !== inventory.length) {
|
|
92
|
+
throw new Error('Invalid Pro2 resource inventory: duplicate resource type');
|
|
93
|
+
}
|
|
94
|
+
return PROTOCOL_V2_RESOURCE_TYPES.flatMap(type => {
|
|
95
|
+
const item = inventory.find(candidate => candidate.type === type);
|
|
96
|
+
return item ? [item] : [];
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function requestProtocolV2ResourceInventory({
|
|
101
|
+
commands,
|
|
102
|
+
timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS,
|
|
103
|
+
}: {
|
|
104
|
+
commands: DeviceCommands;
|
|
105
|
+
timeoutMs?: number;
|
|
106
|
+
}): Promise<ProtocolV2ResourceInventoryItem[]> {
|
|
107
|
+
const { message } = await commands.typedCall(
|
|
108
|
+
'ResourceInventoryGet',
|
|
109
|
+
'ResourceInventory',
|
|
110
|
+
{},
|
|
111
|
+
{ timeoutMs }
|
|
112
|
+
);
|
|
113
|
+
return parseProtocolV2ResourceInventory(message);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function normalizeHex(value: unknown, expectedLength: number, field: string): string {
|
|
117
|
+
if (typeof value !== 'string') {
|
|
118
|
+
throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
|
|
119
|
+
}
|
|
120
|
+
const normalized = value.replace(/^0x/i, '').toLowerCase();
|
|
121
|
+
if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return normalized;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function validateResource(value: unknown, index: number): IProtocolV2Resource {
|
|
130
|
+
if (!value || typeof value !== 'object') {
|
|
131
|
+
throw new Error(`Invalid Pro2 resource at stable[${index}]`);
|
|
132
|
+
}
|
|
133
|
+
const resource = value as Partial<IProtocolV2Resource>;
|
|
134
|
+
if (typeof resource.type !== 'string' || !RESOURCE_TYPE_SET.has(resource.type)) {
|
|
135
|
+
throw new Error(`Invalid Pro2 resource type at stable[${index}]`);
|
|
136
|
+
}
|
|
137
|
+
if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
|
|
138
|
+
throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
|
|
139
|
+
}
|
|
140
|
+
if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
|
|
141
|
+
throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
type: resource.type,
|
|
145
|
+
url: resource.url,
|
|
146
|
+
size: Number(resource.size),
|
|
147
|
+
fileHash: normalizeHex(resource.fileHash, SHA256_HEX_LENGTH, 'fileHash'),
|
|
148
|
+
headerHash: normalizeHex(resource.headerHash, SHA3_512_HEX_LENGTH, 'headerHash'),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Validate a complete Pro2 stable resource set from remote configuration. */
|
|
153
|
+
export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources | undefined {
|
|
154
|
+
if (value === undefined) return undefined;
|
|
155
|
+
if (
|
|
156
|
+
!value ||
|
|
157
|
+
typeof value !== 'object' ||
|
|
158
|
+
!Array.isArray((value as { stable?: unknown }).stable)
|
|
159
|
+
) {
|
|
160
|
+
throw new Error('Invalid Pro2 resources config: stable must be an array');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const stable = (value as { stable: unknown[] }).stable.map(validateResource);
|
|
164
|
+
const types = new Set(stable.map(resource => resource.type));
|
|
165
|
+
if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
|
|
166
|
+
throw new Error('Invalid Pro2 resources config: stable must contain six unique resource types');
|
|
167
|
+
}
|
|
168
|
+
for (const type of PROTOCOL_V2_RESOURCE_TYPES) {
|
|
169
|
+
if (!types.has(type)) {
|
|
170
|
+
throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
|
|
175
|
+
const resource = stable.find(item => item.type === type);
|
|
176
|
+
if (!resource) {
|
|
177
|
+
throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
|
|
178
|
+
}
|
|
179
|
+
return resource;
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Compare the application inventory or select the full set for bootloader recovery. */
|
|
185
|
+
export function buildProtocolV2ResourceUpdatePlan({
|
|
186
|
+
resources,
|
|
187
|
+
inventory,
|
|
188
|
+
mode,
|
|
189
|
+
forced = false,
|
|
190
|
+
}: {
|
|
191
|
+
resources: readonly IProtocolV2Resource[];
|
|
192
|
+
inventory?: readonly ProtocolV2ResourceInventoryItem[];
|
|
193
|
+
mode: ProtocolV2ResourceUpdateMode;
|
|
194
|
+
forced?: boolean;
|
|
195
|
+
}): ProtocolV2ResourceUpdatePlan {
|
|
196
|
+
if (mode === 'bootloader-recovery' || forced) {
|
|
197
|
+
return {
|
|
198
|
+
status: resources.length > 0 ? 'outdated' : 'valid',
|
|
199
|
+
resources: [...resources],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (!inventory) {
|
|
203
|
+
return { status: 'unknown', resources: [] };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const inventoryByType = new Map(inventory.map(item => [item.type, item]));
|
|
207
|
+
const changedResources = resources.filter(resource => {
|
|
208
|
+
const current = inventoryByType.get(resource.type);
|
|
209
|
+
return (
|
|
210
|
+
!current ||
|
|
211
|
+
current.size !== resource.size ||
|
|
212
|
+
current.headerHash.toLowerCase() !== resource.headerHash.toLowerCase()
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
status: changedResources.length === 0 ? 'valid' : 'outdated',
|
|
218
|
+
resources: changedResources,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function bytesToHex(bytes: Uint8Array): string {
|
|
223
|
+
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Verify the complete downloaded file before any device mutation. */
|
|
227
|
+
export function isProtocolV2ResourceFileValid(
|
|
228
|
+
binary: ArrayBuffer,
|
|
229
|
+
resource: Pick<IProtocolV2Resource, 'size' | 'fileHash'>
|
|
230
|
+
): boolean {
|
|
231
|
+
if (binary.byteLength !== resource.size) return false;
|
|
232
|
+
return bytesToHex(sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
|
|
233
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UI_REQUEST, createUiMessage } from '../../events';
|
|
2
2
|
|
|
3
|
-
import type { CoreMessage, ProtocolV2UiCompletion } from '../../events';
|
|
3
|
+
import type { CoreMessage, HardwareUiInteractionMeta, ProtocolV2UiCompletion } from '../../events';
|
|
4
4
|
import type { Device } from '../../device/Device';
|
|
5
5
|
import type { KnownDevice } from '../../types';
|
|
6
6
|
|
|
@@ -28,7 +28,15 @@ type ProtocolV2InteractionMethod = {
|
|
|
28
28
|
protocolV2UiInteraction?: ProtocolV2InteractionDescriptor;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
type InteractionDevice = Pick<Device, 'isProtocolV2' | 'toMessageObject'
|
|
31
|
+
type InteractionDevice = Pick<Device, 'isProtocolV2' | 'toMessageObject'> &
|
|
32
|
+
Partial<
|
|
33
|
+
Pick<
|
|
34
|
+
Device,
|
|
35
|
+
| 'createProtocolV2UiPhaseMetadata'
|
|
36
|
+
| 'finishProtocolV2UiInteraction'
|
|
37
|
+
| 'hasOpenProtocolV2UiInteraction'
|
|
38
|
+
>
|
|
39
|
+
>;
|
|
32
40
|
type PostMessage = (message: CoreMessage) => void;
|
|
33
41
|
|
|
34
42
|
export const isProtocolV2UiEnabled = (method: { protocolV2UiMode?: 'auto' | 'none' }) =>
|
|
@@ -156,12 +164,17 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
156
164
|
this.methodInteraction = interaction;
|
|
157
165
|
if (!interaction) return;
|
|
158
166
|
const { request, ...metadata } = interaction;
|
|
167
|
+
const interactionMeta = this.device.createProtocolV2UiPhaseMetadata?.(
|
|
168
|
+
request === 'pin' ? 'pin' : 'button',
|
|
169
|
+
'start'
|
|
170
|
+
);
|
|
159
171
|
|
|
160
172
|
this.emit(
|
|
161
173
|
request === 'pin' ? UI_REQUEST.REQUEST_PIN : UI_REQUEST.REQUEST_BUTTON,
|
|
162
174
|
{
|
|
163
175
|
device: this.device.toMessageObject() as KnownDevice,
|
|
164
176
|
...metadata,
|
|
177
|
+
...(interactionMeta ? { interaction: interactionMeta } : {}),
|
|
165
178
|
},
|
|
166
179
|
`method:${request}:${interaction.reason}:${interaction.page ?? ''}:${
|
|
167
180
|
interaction.operation ?? ''
|
|
@@ -169,7 +182,8 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
169
182
|
);
|
|
170
183
|
}
|
|
171
184
|
|
|
172
|
-
enterUnlockInteraction(method?: string) {
|
|
185
|
+
enterUnlockInteraction(method?: string): HardwareUiInteractionMeta | undefined {
|
|
186
|
+
const interaction = this.device.createProtocolV2UiPhaseMetadata?.('pin', 'start');
|
|
173
187
|
this.emit(
|
|
174
188
|
UI_REQUEST.REQUEST_PIN,
|
|
175
189
|
{
|
|
@@ -178,9 +192,11 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
178
192
|
reason: 'device-locked',
|
|
179
193
|
deviceOnly: true,
|
|
180
194
|
method,
|
|
195
|
+
...(interaction ? { interaction } : {}),
|
|
181
196
|
},
|
|
182
197
|
`unlock:${method ?? ''}`
|
|
183
198
|
);
|
|
199
|
+
return interaction;
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
resumeMethodInteraction() {
|
|
@@ -188,9 +204,19 @@ export class ProtocolV2UiInteractionCoordinator {
|
|
|
188
204
|
}
|
|
189
205
|
|
|
190
206
|
close() {
|
|
191
|
-
if (
|
|
207
|
+
if (
|
|
208
|
+
!this.device.isProtocolV2() ||
|
|
209
|
+
(!this.opened && !this.device.hasOpenProtocolV2UiInteraction?.()) ||
|
|
210
|
+
this.closed
|
|
211
|
+
)
|
|
212
|
+
return;
|
|
192
213
|
this.closed = true;
|
|
193
|
-
this.
|
|
214
|
+
const interaction = this.device.finishProtocolV2UiInteraction?.();
|
|
215
|
+
this.postMessage(
|
|
216
|
+
interaction
|
|
217
|
+
? createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW, interaction)
|
|
218
|
+
: createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW)
|
|
219
|
+
);
|
|
194
220
|
}
|
|
195
221
|
|
|
196
222
|
private emit(
|
|
@@ -1,105 +1 @@
|
|
|
1
|
-
export type UnlockPolicy = 'none' | 'unlock-before-run'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Protocol V2 methods that are safe to replay after a locked response.
|
|
5
|
-
*
|
|
6
|
-
* Keep this as an exact allowlist. Name-pattern matching can silently make a new
|
|
7
|
-
* state-changing method replayable when it happens to share a suffix.
|
|
8
|
-
*/
|
|
9
|
-
export const PROTOCOL_V2_RETRY_ON_LOCKED_METHODS = [
|
|
10
|
-
'cipherKeyValue',
|
|
11
|
-
'allNetworkGetAddress',
|
|
12
|
-
'allNetworkGetAddressByLoop',
|
|
13
|
-
'btcGetAddress',
|
|
14
|
-
'btcGetPublicKey',
|
|
15
|
-
'btcSignMessage',
|
|
16
|
-
'btcSignPsbt',
|
|
17
|
-
'btcSignTransaction',
|
|
18
|
-
'btcVerifyMessage',
|
|
19
|
-
'confluxGetAddress',
|
|
20
|
-
'confluxSignMessage',
|
|
21
|
-
'confluxSignTransaction',
|
|
22
|
-
'evmGetAddress',
|
|
23
|
-
'evmGetPublicKey',
|
|
24
|
-
'evmSignMessage',
|
|
25
|
-
'evmSignTransaction',
|
|
26
|
-
'evmSignTypedData',
|
|
27
|
-
'evmVerifyMessage',
|
|
28
|
-
'starcoinGetAddress',
|
|
29
|
-
'starcoinGetPublicKey',
|
|
30
|
-
'starcoinSignMessage',
|
|
31
|
-
'starcoinSignTransaction',
|
|
32
|
-
'starcoinVerifyMessage',
|
|
33
|
-
'nemGetAddress',
|
|
34
|
-
'nemSignTransaction',
|
|
35
|
-
'solGetAddress',
|
|
36
|
-
'solSignTransaction',
|
|
37
|
-
'solSignOffchainMessage',
|
|
38
|
-
'solSignMessage',
|
|
39
|
-
'stellarGetAddress',
|
|
40
|
-
'stellarSignTransaction',
|
|
41
|
-
'tronGetAddress',
|
|
42
|
-
'tronSignMessage',
|
|
43
|
-
'tronSignTransaction',
|
|
44
|
-
'nearGetAddress',
|
|
45
|
-
'nearSignTransaction',
|
|
46
|
-
'aptosGetAddress',
|
|
47
|
-
'aptosGetPublicKey',
|
|
48
|
-
'aptosSignTransaction',
|
|
49
|
-
'aptosSignMessage',
|
|
50
|
-
'aptosSignInMessage',
|
|
51
|
-
'algoGetAddress',
|
|
52
|
-
'algoSignTransaction',
|
|
53
|
-
'cosmosGetAddress',
|
|
54
|
-
'cosmosGetPublicKey',
|
|
55
|
-
'cosmosSignTransaction',
|
|
56
|
-
'xrpGetAddress',
|
|
57
|
-
'xrpSignTransaction',
|
|
58
|
-
'suiGetAddress',
|
|
59
|
-
'suiGetPublicKey',
|
|
60
|
-
'suiSignMessage',
|
|
61
|
-
'suiSignTransaction',
|
|
62
|
-
'cardanoGetAddress',
|
|
63
|
-
'cardanoGetPublicKey',
|
|
64
|
-
'cardanoSignTransaction',
|
|
65
|
-
'cardanoSignMessage',
|
|
66
|
-
'filecoinGetAddress',
|
|
67
|
-
'filecoinSignTransaction',
|
|
68
|
-
'polkadotGetAddress',
|
|
69
|
-
'polkadotSignTransaction',
|
|
70
|
-
'kaspaGetAddress',
|
|
71
|
-
'kaspaSignTransaction',
|
|
72
|
-
'nexaGetAddress',
|
|
73
|
-
'nexaSignTransaction',
|
|
74
|
-
'nostrGetPublicKey',
|
|
75
|
-
'nostrSignEvent',
|
|
76
|
-
'nostrEncryptMessage',
|
|
77
|
-
'nostrDecryptMessage',
|
|
78
|
-
'nostrSignSchnorr',
|
|
79
|
-
'lnurlAuth',
|
|
80
|
-
'nervosGetAddress',
|
|
81
|
-
'nervosSignTransaction',
|
|
82
|
-
'dnxGetAddress',
|
|
83
|
-
'dnxSignTransaction',
|
|
84
|
-
'tonGetAddress',
|
|
85
|
-
'tonSignMessage',
|
|
86
|
-
'tonSignProof',
|
|
87
|
-
'tonSignData',
|
|
88
|
-
'scdoGetAddress',
|
|
89
|
-
'scdoSignTransaction',
|
|
90
|
-
'scdoSignMessage',
|
|
91
|
-
'alephiumGetAddress',
|
|
92
|
-
'alephiumSignTransaction',
|
|
93
|
-
'alephiumSignMessage',
|
|
94
|
-
'benfenGetAddress',
|
|
95
|
-
'benfenGetPublicKey',
|
|
96
|
-
'benfenSignMessage',
|
|
97
|
-
'benfenSignTransaction',
|
|
98
|
-
'neoGetAddress',
|
|
99
|
-
'neoSignTransaction',
|
|
100
|
-
] as const;
|
|
101
|
-
|
|
102
|
-
const retryOnLockedMethods = new Set<string>(PROTOCOL_V2_RETRY_ON_LOCKED_METHODS);
|
|
103
|
-
|
|
104
|
-
export const getProtocolV2UnlockPolicy = (methodName: string): UnlockPolicy =>
|
|
105
|
-
retryOnLockedMethods.has(methodName) ? 'retry-on-locked' : 'none';
|
|
1
|
+
export type UnlockPolicy = 'none' | 'unlock-before-run';
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import { LoggerNames, getLogger } from '../../utils';
|
|
4
|
+
import { isProtocolV2UiEnabled, resolveProtocolV2UiInteraction } from './uiInteraction';
|
|
5
|
+
import { refreshProtocolV2DeviceStatus } from './walletSession';
|
|
6
|
+
|
|
7
|
+
import type { BaseMethod } from '../../api/BaseMethod';
|
|
8
|
+
import type { Device } from '../../device/Device';
|
|
9
|
+
import type { HardwareUiInteractionMeta } from '../../events';
|
|
10
|
+
import type { ProtocolV2UiInteractionCoordinator } from './uiInteraction';
|
|
11
|
+
|
|
12
|
+
const Log = getLogger(LoggerNames.Core);
|
|
13
|
+
|
|
14
|
+
export type ProtocolV2UnlockContext = {
|
|
15
|
+
preflightCompleted: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const createProtocolV2UnlockContext = (): ProtocolV2UnlockContext => ({
|
|
19
|
+
preflightCompleted: false,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
type RunnableMethod = Pick<
|
|
23
|
+
BaseMethod,
|
|
24
|
+
| 'run'
|
|
25
|
+
| 'unlockPolicy'
|
|
26
|
+
| 'useDevicePassphraseState'
|
|
27
|
+
| 'protocolV2UiInteraction'
|
|
28
|
+
| 'protocolV2UiMode'
|
|
29
|
+
| 'params'
|
|
30
|
+
| 'payload'
|
|
31
|
+
> & {
|
|
32
|
+
name?: string;
|
|
33
|
+
protocolV2UnlockContext?: ProtocolV2UnlockContext;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type UiInteractionCoordinator = Pick<
|
|
37
|
+
ProtocolV2UiInteractionCoordinator,
|
|
38
|
+
'enterMethodInteraction' | 'enterUnlockInteraction'
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
type RunMethodWithUnlockPolicyOptions<T> = {
|
|
42
|
+
context?: ProtocolV2UnlockContext;
|
|
43
|
+
uiCoordinator?: UiInteractionCoordinator;
|
|
44
|
+
afterStatusBeforeUnlock?: () => void | Promise<void>;
|
|
45
|
+
prepare?: () => Promise<void>;
|
|
46
|
+
run?: () => Promise<T>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export async function runMethodWithUnlockPolicy<T = unknown>(
|
|
50
|
+
method: RunnableMethod,
|
|
51
|
+
device: Device,
|
|
52
|
+
options: RunMethodWithUnlockPolicyOptions<T> = {}
|
|
53
|
+
): Promise<T> {
|
|
54
|
+
const {
|
|
55
|
+
context = createProtocolV2UnlockContext(),
|
|
56
|
+
uiCoordinator,
|
|
57
|
+
afterStatusBeforeUnlock,
|
|
58
|
+
prepare,
|
|
59
|
+
run: configuredRun,
|
|
60
|
+
} = options;
|
|
61
|
+
const run = configuredRun ?? (() => method.run() as Promise<T>);
|
|
62
|
+
method.protocolV2UnlockContext = context;
|
|
63
|
+
|
|
64
|
+
const shouldEmitUi = isProtocolV2UiEnabled(method);
|
|
65
|
+
const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
|
|
66
|
+
const requiresPreUnlock =
|
|
67
|
+
device.isProtocolV2() &&
|
|
68
|
+
(method.useDevicePassphraseState || method.unlockPolicy === 'unlock-before-run') &&
|
|
69
|
+
!device.isBootloader?.() &&
|
|
70
|
+
!device.isRomloader?.();
|
|
71
|
+
|
|
72
|
+
if (requiresPreUnlock && !context.preflightCompleted) {
|
|
73
|
+
const status = await refreshProtocolV2DeviceStatus(device);
|
|
74
|
+
if (typeof status?.unlocked !== 'boolean') {
|
|
75
|
+
throw ERRORS.TypedError(
|
|
76
|
+
HardwareErrorCode.RuntimeError,
|
|
77
|
+
'Protocol V2 DeviceStatus did not report an explicit unlock state.'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await afterStatusBeforeUnlock?.();
|
|
82
|
+
|
|
83
|
+
if (!status.unlocked) {
|
|
84
|
+
const unlockInteraction: HardwareUiInteractionMeta | undefined = shouldCoordinateUi
|
|
85
|
+
? uiCoordinator.enterUnlockInteraction(method.name)
|
|
86
|
+
: undefined;
|
|
87
|
+
const unlockedStatus = await device.unlockDevice(
|
|
88
|
+
undefined,
|
|
89
|
+
shouldCoordinateUi
|
|
90
|
+
? { emitUiEvent: false, interaction: unlockInteraction }
|
|
91
|
+
: {
|
|
92
|
+
source: 'unlock-coordinator',
|
|
93
|
+
reason: 'device-locked',
|
|
94
|
+
deviceOnly: true,
|
|
95
|
+
method: method.name,
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
if (unlockedStatus?.unlocked !== true) {
|
|
99
|
+
throw ERRORS.TypedError(
|
|
100
|
+
HardwareErrorCode.RuntimeError,
|
|
101
|
+
'Protocol V2 device remained locked after the unlock flow.'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
Log.debug('Protocol V2 pre-unlock completed', { method: method.name });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
context.preflightCompleted = true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
await prepare?.();
|
|
111
|
+
|
|
112
|
+
if (shouldCoordinateUi) {
|
|
113
|
+
uiCoordinator.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return run();
|
|
117
|
+
}
|