@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.
- package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +313 -0
- package/__tests__/DeviceCommands.test.ts +59 -13
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +198 -3
- package/__tests__/core-initialization.test.ts +23 -0
- package/__tests__/device-connector-protocol.test.ts +81 -0
- package/__tests__/device-lifecycle-events.test.ts +105 -3
- package/__tests__/device-pool-state.test.ts +22 -0
- package/__tests__/device-settings.test.ts +39 -18
- package/__tests__/device-state-contract.test.ts +1 -0
- package/__tests__/device-state-mapper.test.ts +13 -1
- package/__tests__/device-utils.test.ts +48 -1
- package/__tests__/deviceSettings.test.ts +11 -1
- package/__tests__/deviceUploadNft.test.ts +316 -0
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +5 -0
- package/__tests__/get-device-state.test.ts +132 -12
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/kaspa-use-tweak-pro2.test.ts +2 -2
- package/__tests__/kaspaSignTransaction.test.ts +3 -26
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/method-protocol-support.test.ts +19 -0
- package/__tests__/open-wallet-session.test.ts +568 -37
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-binding.test.ts +89 -0
- package/__tests__/protocol-v2-resources.test.ts +340 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +300 -37
- package/__tests__/protocol-v2.test.ts +1449 -867
- package/__tests__/protocolV2FileWrite.test.ts +152 -0
- package/__tests__/protocolV2UiInteraction.test.ts +63 -0
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/search-devices.test.ts +12 -3
- package/__tests__/tron-sign-message-init.test.ts +2 -2
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/__tests__/uiPromiseRegistry.test.ts +115 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
- package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +11 -4
- 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/SearchDevices.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +2 -2
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +4 -3
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +9 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaGetAddress.d.ts +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
- package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -1
- package/dist/api/polkadot/networks.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/solana/SolSignMessage.d.ts +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -1
- package/dist/api/solana/SolSignTransaction.d.ts +2 -2
- package/dist/api/sui/SuiGetAddress.d.ts +1 -1
- package/dist/api/sui/SuiGetPublicKey.d.ts +1 -1
- package/dist/api/sui/SuiSignMessage.d.ts +1 -1
- package/dist/api/sui/SuiSignTransaction.d.ts +1 -1
- package/dist/api/ton/TonGetAddress.d.ts +1 -1
- package/dist/api/ton/TonSignMessage.d.ts +1 -1
- package/dist/api/ton/TonSignProof.d.ts +1 -1
- package/dist/api/tron/TronSignMessage.d.ts +2 -2
- package/dist/api/tron/TronSignTransaction.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/core/uiPromiseRegistry.d.ts +6 -0
- package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
- package/dist/data-manager/DataManager.d.ts +9 -3
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/device/Device.d.ts +29 -5
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +2 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/deviceProfile/protocolV2DeviceIdentity.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-promise.d.ts +4 -2
- package/dist/events/ui-promise.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +31 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/events/ui-response.d.ts +10 -2
- package/dist/events/ui-response.d.ts.map +1 -1
- package/dist/index.d.ts +243 -69
- package/dist/index.js +2563 -1217
- package/dist/inject.d.ts +6 -1
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +0 -4
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +30 -0
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +6 -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 +3 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
- package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/types/api/deviceUnlock.d.ts +5 -1
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +1 -0
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +4 -2
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +6 -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/types/device.d.ts +2 -2
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts +1 -0
- package/dist/types/params.d.ts.map +1 -1
- package/dist/types/settings.d.ts +38 -8
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +31 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +8 -10
- package/src/api/CheckAllFirmwareRelease.ts +61 -11
- package/src/api/DetectDeviceConnectProtocol.ts +18 -0
- package/src/api/FirmwareUpdate.ts +6 -2
- package/src/api/FirmwareUpdateV2.ts +5 -2
- package/src/api/FirmwareUpdateV3.ts +6 -2
- package/src/api/FirmwareUpdateV4.ts +463 -258
- package/src/api/GetPassphraseState.ts +7 -1
- package/src/api/OpenWalletSession.ts +71 -17
- package/src/api/SearchDevices.ts +3 -1
- package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +36 -11
- package/src/api/device/DeviceSettings.ts +5 -4
- package/src/api/device/DeviceUnlock.ts +8 -3
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -12
- package/src/api/firmware/uploadFirmware.ts +17 -4
- package/src/api/helpers/protocolV2FileWrite.ts +202 -63
- package/src/api/index.ts +2 -0
- package/src/api/kaspa/KaspaGetAddress.ts +1 -1
- package/src/api/kaspa/KaspaSignTransaction.ts +2 -13
- package/src/api/polkadot/networks.ts +3 -3
- package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/api/protocol-v2/helpers.ts +1 -1
- package/src/api/solana/SolSignMessage.ts +1 -1
- package/src/api/solana/SolSignOffchainMessage.ts +1 -1
- package/src/api/solana/SolSignTransaction.ts +2 -2
- package/src/api/sui/SuiGetAddress.ts +1 -1
- package/src/api/sui/SuiGetPublicKey.ts +1 -1
- package/src/api/sui/SuiSignMessage.ts +1 -1
- package/src/api/sui/SuiSignTransaction.ts +1 -1
- package/src/api/ton/TonGetAddress.ts +1 -1
- package/src/api/ton/TonSignMessage.ts +1 -1
- package/src/api/ton/TonSignProof.ts +1 -1
- package/src/api/tron/TronSignMessage.ts +2 -2
- package/src/api/tron/TronSignTransaction.ts +1 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +213 -111
- package/src/core/uiPromiseRegistry.ts +63 -0
- package/src/data/messages/messages-protocol-v2.json +25 -0
- package/src/data-manager/DataManager.ts +88 -11
- package/src/data-manager/TransportManager.ts +6 -0
- package/src/device/Device.ts +273 -43
- package/src/device/DeviceCommands.ts +31 -4
- package/src/device/DeviceConnector.ts +33 -13
- package/src/device/DevicePool.ts +21 -7
- package/src/deviceProfile/buildDeviceFeatures.ts +2 -2
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +3 -0
- package/src/events/device.ts +4 -0
- package/src/events/logBlockEvent.ts +1 -0
- package/src/events/ui-promise.ts +4 -2
- package/src/events/ui-request.ts +36 -2
- package/src/events/ui-response.ts +12 -2
- package/src/inject.ts +58 -2
- package/src/lowLevelInject.ts +6 -3
- package/src/protocols/protocol-v2/features.ts +10 -7
- package/src/protocols/protocol-v2/firmware.ts +2 -0
- package/src/protocols/protocol-v2/resources.ts +390 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
- package/src/protocols/protocol-v2/walletSession.ts +214 -36
- package/src/topLevelInject.ts +6 -3
- package/src/types/api/checkAllFirmwareRelease.ts +3 -1
- package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
- package/src/types/api/deviceUnlock.ts +12 -1
- package/src/types/api/export.ts +1 -0
- package/src/types/api/firmwareUpdate.ts +6 -3
- package/src/types/api/index.ts +13 -0
- package/src/types/api/protocolV2.ts +13 -0
- package/src/types/device.ts +15 -3
- package/src/types/params.ts +7 -1
- package/src/types/settings.ts +60 -9
- package/src/utils/deviceFeaturesCompat.ts +5 -0
- package/src/utils/deviceFeaturesUtils.ts +14 -3
- package/src/utils/deviceInfoUtils.ts +2 -0
- package/src/utils/deviceSettings.ts +3 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +142 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/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 -109
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { DeviceType } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
getDeviceSerialNo,
|
|
5
6
|
getDeviceType,
|
|
7
|
+
getDeviceTypeByBleName,
|
|
6
8
|
getDeviceUUID,
|
|
7
9
|
getFirmwareType,
|
|
10
|
+
getMethodVersionRange,
|
|
8
11
|
} from '../src/utils/deviceInfoUtils';
|
|
9
12
|
import {
|
|
10
13
|
getDeviceBLEFirmwareVersion,
|
|
@@ -85,7 +88,7 @@ describe('device feature selectors', () => {
|
|
|
85
88
|
test('reads normalized Protocol V2 features through the same public selectors', () => {
|
|
86
89
|
const features = buildProtocolV2FeaturesPayload({
|
|
87
90
|
deviceInfo: {
|
|
88
|
-
hw: { serial_no: 'P2-001' },
|
|
91
|
+
hw: { Device_type: DeviceType.PRO2, serial_no: 'P2-001' },
|
|
89
92
|
fw: {
|
|
90
93
|
application: { version: '5.0.0' },
|
|
91
94
|
bootloader: { version: '2.0.0' },
|
|
@@ -104,4 +107,48 @@ describe('device feature selectors', () => {
|
|
|
104
107
|
expect(getDeviceBoardloaderVersion(features)).toEqual([1, 0, 0]);
|
|
105
108
|
expect(getDeviceBLEFirmwareVersion(features)).toEqual([3, 0, 0]);
|
|
106
109
|
});
|
|
110
|
+
|
|
111
|
+
test('preserves the real Neo type in Protocol V2 compatibility features', () => {
|
|
112
|
+
const features = buildProtocolV2FeaturesPayload({
|
|
113
|
+
deviceInfo: {
|
|
114
|
+
hw: { Device_type: DeviceType.NEO, serial_no: 'NEO-001' },
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(features.deviceType).toBe(EDeviceType.Neo);
|
|
119
|
+
expect(features.onekey_device_type).toBe('NEO');
|
|
120
|
+
expect(getDeviceType(features)).toBe(EDeviceType.Neo);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('recognizes Neo discovery names before initialization', () => {
|
|
124
|
+
expect(getDeviceTypeByBleName('OneKey Neo 1234')).toBe(EDeviceType.Neo);
|
|
125
|
+
expect(getDeviceTypeByBleName('Neo 1234')).toBe(EDeviceType.Neo);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test.each([EDeviceType.Pro2, EDeviceType.Neo])(
|
|
129
|
+
'uses the Pro2 product model as the chain capability fallback for %s',
|
|
130
|
+
deviceType => {
|
|
131
|
+
const checkedTypes: string[] = [];
|
|
132
|
+
const versionRange = getMethodVersionRange({ deviceType } as PROTO.Features, type => {
|
|
133
|
+
checkedTypes.push(type);
|
|
134
|
+
return type === 'model_pro2' ? { min: '1.0.0' } : undefined;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(versionRange).toEqual({ min: '1.0.0' });
|
|
138
|
+
expect(checkedTypes).toEqual([deviceType, 'model_pro2']);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
test('prefers a Neo-specific chain range over the shared Pro2 product model', () => {
|
|
143
|
+
const versionRange = getMethodVersionRange(
|
|
144
|
+
{ deviceType: EDeviceType.Neo } as PROTO.Features,
|
|
145
|
+
type => {
|
|
146
|
+
if (type === EDeviceType.Neo) return { min: '2.0.0' };
|
|
147
|
+
if (type === 'model_pro2') return { min: '1.0.0' };
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(versionRange).toEqual({ min: '2.0.0' });
|
|
153
|
+
});
|
|
107
154
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
PROTOCOL_V2_NEVER_TIMEOUT_MS,
|
|
4
5
|
getAutoLockOptions,
|
|
5
6
|
getAutoShutDownOptions,
|
|
6
7
|
getDeviceSettingsCapabilities,
|
|
7
8
|
getLanguageConfig,
|
|
8
9
|
mapLanguageFromProtocolV2,
|
|
9
10
|
mapLanguageToProtocolV2,
|
|
10
|
-
PROTOCOL_V2_NEVER_TIMEOUT_MS,
|
|
11
11
|
} from '../src/utils/deviceSettings';
|
|
12
12
|
|
|
13
13
|
describe('Pro 2 device settings options', () => {
|
|
@@ -72,6 +72,16 @@ describe('Pro 2 device settings options', () => {
|
|
|
72
72
|
]);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
test('uses the shared Protocol V2 settings model for Neo', () => {
|
|
76
|
+
expect(getLanguageConfig(EDeviceType.Neo)).toEqual(getLanguageConfig(EDeviceType.Pro2));
|
|
77
|
+
expect(getAutoLockOptions(EDeviceType.Neo, 'V2')).toEqual(
|
|
78
|
+
getAutoLockOptions(EDeviceType.Pro2, 'V2')
|
|
79
|
+
);
|
|
80
|
+
expect(getAutoShutDownOptions(EDeviceType.Neo, 'V2')).toEqual(
|
|
81
|
+
getAutoShutDownOptions(EDeviceType.Pro2, 'V2')
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
75
85
|
test('keeps the legacy and Protocol V2 never values protocol-specific', () => {
|
|
76
86
|
expect(getAutoLockOptions(EDeviceType.Pro, 'V1').slice(-1)[0]).toEqual({
|
|
77
87
|
label: 'Never',
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import DeviceUploadNft from '../src/api/protocol-v2/DeviceUploadNft';
|
|
4
|
+
|
|
5
|
+
jest.mock('../src/data/config', () => ({
|
|
6
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
7
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
const createRgba = (width: number, height: number) => {
|
|
11
|
+
const data = new Uint8Array(width * height * 4);
|
|
12
|
+
for (let index = 3; index < data.length; index += 4) data[index] = 0xff;
|
|
13
|
+
return data;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const createMethod = ({
|
|
17
|
+
typedCall,
|
|
18
|
+
supportedMessages = [60802, 60805, 60808, 61500],
|
|
19
|
+
useFullBundle = false,
|
|
20
|
+
}: {
|
|
21
|
+
typedCall: jest.Mock;
|
|
22
|
+
supportedMessages?: number[];
|
|
23
|
+
useFullBundle?: boolean;
|
|
24
|
+
}) => {
|
|
25
|
+
const method = new DeviceUploadNft({
|
|
26
|
+
id: 1,
|
|
27
|
+
payload: {
|
|
28
|
+
method: 'deviceUploadNft',
|
|
29
|
+
image: { width: 540, height: 540, rgba: createRgba(540, 540) },
|
|
30
|
+
thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
|
|
31
|
+
title: 'CryptoPunk #3100',
|
|
32
|
+
subtitle: 'CryptoPunks',
|
|
33
|
+
timestampMs: 1_760_000_000_000,
|
|
34
|
+
chunkSize: 2048,
|
|
35
|
+
paceMs: 0,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
(method as any).device = {
|
|
39
|
+
commands: { typedCall },
|
|
40
|
+
ensureProtocolV2RuntimeContext: jest.fn(() =>
|
|
41
|
+
Promise.resolve({
|
|
42
|
+
version: 2,
|
|
43
|
+
build_fingerprint: 'application__1.0.0__test__DEV__DEBUG',
|
|
44
|
+
supported_messages: supportedMessages,
|
|
45
|
+
})
|
|
46
|
+
),
|
|
47
|
+
getCurrentFirmwareType: jest.fn(),
|
|
48
|
+
};
|
|
49
|
+
method.postMessage = jest.fn();
|
|
50
|
+
|
|
51
|
+
if (useFullBundle) {
|
|
52
|
+
method.init();
|
|
53
|
+
} else {
|
|
54
|
+
(method as any).params = { chunkSize: 2048, paceMs: 0, timeoutMs: 15_000 };
|
|
55
|
+
(method as any).bundle = {
|
|
56
|
+
basename: 'nft-deadbeef-1760000000000',
|
|
57
|
+
image: new Uint8Array([1]),
|
|
58
|
+
thumbnail: new Uint8Array([2]),
|
|
59
|
+
metadata: new TextEncoder().encode('{"title":"NFT","subtitle":""}'),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return method;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const fileWriteSuccess = (params: { file: { offset: number; data: Uint8Array } }) => ({
|
|
67
|
+
message: {
|
|
68
|
+
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const nftFileList = (count: number, basenameAt?: number) =>
|
|
73
|
+
Array.from({ length: count }, (_, index) => {
|
|
74
|
+
const basename =
|
|
75
|
+
index === basenameAt
|
|
76
|
+
? 'nft-deadbeef-1760000000000'
|
|
77
|
+
: `nft-${index.toString(16).padStart(8, '0')}-${1760000000001 + index}`;
|
|
78
|
+
return [`${basename}.bin`, `${basename}_m.bin`, `${basename}.json`];
|
|
79
|
+
})
|
|
80
|
+
.flat()
|
|
81
|
+
.join('\n');
|
|
82
|
+
|
|
83
|
+
describe('DeviceUploadNft', () => {
|
|
84
|
+
test('defaults to maximum-size chunks without artificial pacing', () => {
|
|
85
|
+
const method = new DeviceUploadNft({
|
|
86
|
+
id: 1,
|
|
87
|
+
payload: {
|
|
88
|
+
method: 'deviceUploadNft',
|
|
89
|
+
image: { width: 540, height: 540, rgba: createRgba(540, 540) },
|
|
90
|
+
thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
|
|
91
|
+
title: 'CryptoPunk #3100',
|
|
92
|
+
subtitle: 'CryptoPunks',
|
|
93
|
+
timestampMs: 1_760_000_000_000,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
method.init();
|
|
98
|
+
|
|
99
|
+
expect((method as any).params).toMatchObject({
|
|
100
|
+
chunkSize: 2048,
|
|
101
|
+
paceMs: 0,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('uploads the triplet in order without creating the firmware-owned directory', async () => {
|
|
106
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
107
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
108
|
+
return { message: { exist: true, directory: true } };
|
|
109
|
+
}
|
|
110
|
+
if (request === 'FilesystemDirList') {
|
|
111
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
112
|
+
}
|
|
113
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
114
|
+
if (request === 'NftUpdate') return { message: { message: 'NFT updated' } };
|
|
115
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
116
|
+
});
|
|
117
|
+
const method = createMethod({ typedCall, useFullBundle: true });
|
|
118
|
+
|
|
119
|
+
const result = await method.run();
|
|
120
|
+
|
|
121
|
+
const requests = typedCall.mock.calls.map(call => call[0]);
|
|
122
|
+
expect(requests[0]).toBe('FilesystemPathInfoQuery');
|
|
123
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
124
|
+
1,
|
|
125
|
+
'FilesystemPathInfoQuery',
|
|
126
|
+
'FilesystemPathInfo',
|
|
127
|
+
{ path: 'vol1:/nft' },
|
|
128
|
+
{ timeoutMs: 15_000 }
|
|
129
|
+
);
|
|
130
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
131
|
+
2,
|
|
132
|
+
'FilesystemDirList',
|
|
133
|
+
'FilesystemDir',
|
|
134
|
+
{ path: 'vol1:/nft', depth: 1 },
|
|
135
|
+
{ timeoutMs: 15_000 }
|
|
136
|
+
);
|
|
137
|
+
expect(requests).not.toContain('FilesystemDirMake');
|
|
138
|
+
expect(requests.at(-1)).toBe('NftUpdate');
|
|
139
|
+
const fileWrites = typedCall.mock.calls.filter(call => call[0] === 'FilesystemFileWrite');
|
|
140
|
+
const paths = fileWrites.map(call => call[2].file.path as string);
|
|
141
|
+
const imagePath = paths[0];
|
|
142
|
+
const thumbnailStart = paths.findIndex(path => path.endsWith('_m.bin'));
|
|
143
|
+
const metadataStart = paths.findIndex(path => path.endsWith('.json'));
|
|
144
|
+
expect(imagePath).toMatch(/^vol1:\/nft\/nft-[a-f0-9]{8}-1760000000000\.bin$/);
|
|
145
|
+
expect(thumbnailStart).toBeGreaterThan(0);
|
|
146
|
+
expect(metadataStart).toBeGreaterThan(thumbnailStart);
|
|
147
|
+
expect(new Set(paths.slice(0, thumbnailStart))).toEqual(new Set([imagePath]));
|
|
148
|
+
expect(new Set(paths.slice(thumbnailStart, metadataStart))).toEqual(
|
|
149
|
+
new Set([result.thumbnailPath])
|
|
150
|
+
);
|
|
151
|
+
expect(typedCall).toHaveBeenLastCalledWith(
|
|
152
|
+
'NftUpdate',
|
|
153
|
+
'Success',
|
|
154
|
+
{ file_name_no_ext: result.basename },
|
|
155
|
+
{ timeoutMs: 15_000 }
|
|
156
|
+
);
|
|
157
|
+
expect(result).toMatchObject({
|
|
158
|
+
nftUpdated: true,
|
|
159
|
+
message: 'NFT updated',
|
|
160
|
+
totalSize: 583_212 + 138_876 + 53,
|
|
161
|
+
});
|
|
162
|
+
expect(method.postMessage).toHaveBeenLastCalledWith({
|
|
163
|
+
event: 'UI_EVENT',
|
|
164
|
+
type: 'ui-device_progress',
|
|
165
|
+
payload: expect.objectContaining({
|
|
166
|
+
progress: 100,
|
|
167
|
+
transferredBytes: result.totalSize,
|
|
168
|
+
totalBytes: result.totalSize,
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('treats a missing NFT directory as empty before the first upload', async () => {
|
|
174
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
175
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
176
|
+
return { message: { exist: false, directory: false } };
|
|
177
|
+
}
|
|
178
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
179
|
+
if (request === 'NftUpdate') return { message: { message: 'NFT updated' } };
|
|
180
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
181
|
+
});
|
|
182
|
+
const method = createMethod({ typedCall });
|
|
183
|
+
|
|
184
|
+
await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
|
|
185
|
+
expect(typedCall.mock.calls.map(call => call[0])).not.toContain('FilesystemDirList');
|
|
186
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileWrite')).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('does not retry a timed-out NftUpdate', async () => {
|
|
190
|
+
const timeout = Object.assign(new Error('Protocol V2 response timeout'), {
|
|
191
|
+
code: 'response-timeout',
|
|
192
|
+
});
|
|
193
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
194
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
195
|
+
return { message: { exist: true, directory: true } };
|
|
196
|
+
}
|
|
197
|
+
if (request === 'FilesystemDirList') {
|
|
198
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
199
|
+
}
|
|
200
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
201
|
+
if (request === 'NftUpdate') throw timeout;
|
|
202
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
203
|
+
});
|
|
204
|
+
const method = createMethod({ typedCall });
|
|
205
|
+
|
|
206
|
+
await expect(method.run()).rejects.toBe(timeout);
|
|
207
|
+
|
|
208
|
+
const updateRequests = typedCall.mock.calls.filter(call => call[0] === 'NftUpdate');
|
|
209
|
+
expect(updateRequests).toHaveLength(1);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('does not retry a non-timeout NftUpdate failure', async () => {
|
|
213
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
214
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
215
|
+
return { message: { exist: true, directory: true } };
|
|
216
|
+
}
|
|
217
|
+
if (request === 'FilesystemDirList') {
|
|
218
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
219
|
+
}
|
|
220
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
221
|
+
if (request === 'NftUpdate') throw new Error('Invalid NFT metadata');
|
|
222
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
223
|
+
});
|
|
224
|
+
const method = createMethod({ typedCall });
|
|
225
|
+
|
|
226
|
+
await expect(method.run()).rejects.toThrow('Invalid NFT metadata');
|
|
227
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'NftUpdate')).toHaveLength(1);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('does not publish when a file write fails', async () => {
|
|
231
|
+
const typedCall = jest.fn((request: string) => {
|
|
232
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
233
|
+
return { message: { exist: true, directory: true } };
|
|
234
|
+
}
|
|
235
|
+
if (request === 'FilesystemDirList') {
|
|
236
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
237
|
+
}
|
|
238
|
+
if (request === 'FilesystemFileWrite') throw new Error('Filesystem full');
|
|
239
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
240
|
+
});
|
|
241
|
+
const method = createMethod({ typedCall });
|
|
242
|
+
|
|
243
|
+
await expect(method.run()).rejects.toThrow('Filesystem full');
|
|
244
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'NftUpdate')).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('rejects unsupported firmware before writing files', async () => {
|
|
248
|
+
const typedCall = jest.fn();
|
|
249
|
+
const method = createMethod({ typedCall, supportedMessages: [60805] });
|
|
250
|
+
|
|
251
|
+
await expect(method.run()).rejects.toBeDefined();
|
|
252
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('rejects a new NFT before writing when ten complete NFT bundles exist', async () => {
|
|
256
|
+
const typedCall = jest.fn((request: string) => {
|
|
257
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
258
|
+
return { message: { exist: true, directory: true } };
|
|
259
|
+
}
|
|
260
|
+
if (request === 'FilesystemDirList') {
|
|
261
|
+
return { message: { path: 'vol1:/nft', child_files: nftFileList(10) } };
|
|
262
|
+
}
|
|
263
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
264
|
+
});
|
|
265
|
+
const method = createMethod({ typedCall });
|
|
266
|
+
|
|
267
|
+
await expect(method.run()).rejects.toMatchObject({
|
|
268
|
+
errorCode: HardwareErrorCode.NftStorageLimitReached,
|
|
269
|
+
params: { count: 10, limit: 10 },
|
|
270
|
+
});
|
|
271
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
272
|
+
'FilesystemPathInfoQuery',
|
|
273
|
+
'FilesystemDirList',
|
|
274
|
+
]);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('allows an idempotent retry for a basename already counted at the limit', async () => {
|
|
278
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
279
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
280
|
+
return { message: { exist: true, directory: true } };
|
|
281
|
+
}
|
|
282
|
+
if (request === 'FilesystemDirList') {
|
|
283
|
+
return { message: { path: 'vol1:/nft', child_files: nftFileList(10, 0) } };
|
|
284
|
+
}
|
|
285
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
286
|
+
if (request === 'NftUpdate') return { message: {} };
|
|
287
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
288
|
+
});
|
|
289
|
+
const method = createMethod({ typedCall });
|
|
290
|
+
|
|
291
|
+
await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
|
|
292
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileWrite')).toBe(true);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('does not count unrelated or incomplete files as stored NFTs', async () => {
|
|
296
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
297
|
+
if (request === 'FilesystemPathInfoQuery') {
|
|
298
|
+
return { message: { exist: true, directory: true } };
|
|
299
|
+
}
|
|
300
|
+
if (request === 'FilesystemDirList') {
|
|
301
|
+
return {
|
|
302
|
+
message: {
|
|
303
|
+
path: 'vol1:/nft',
|
|
304
|
+
child_files: `${nftFileList(9)}\nnft-ffffffff-1760000009999.json\nnotes.txt`,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
309
|
+
if (request === 'NftUpdate') return { message: {} };
|
|
310
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
311
|
+
});
|
|
312
|
+
const method = createMethod({ typedCall });
|
|
313
|
+
|
|
314
|
+
await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
|
|
315
|
+
});
|
|
316
|
+
});
|
|
@@ -34,6 +34,11 @@ describe('FirmwareUpdateV3 reconnect', () => {
|
|
|
34
34
|
});
|
|
35
35
|
method.isBleReconnect = jest.fn(() => false);
|
|
36
36
|
method.device = {
|
|
37
|
+
originalDescriptor: {
|
|
38
|
+
id: 'usb-path',
|
|
39
|
+
path: 'usb-path',
|
|
40
|
+
protocolType: 'V1',
|
|
41
|
+
},
|
|
37
42
|
deviceConnector: {
|
|
38
43
|
enumerate: jest.fn().mockResolvedValue({
|
|
39
44
|
descriptors: [{ path: 'usb-path' }],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
import { DeviceType } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
4
|
import { Device } from '../src/device/Device';
|
|
@@ -43,6 +43,70 @@ const getProtocolV2LoaderInfo = (mode: 'bootloader' | 'romloader') => ({
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
describe('getDeviceState', () => {
|
|
46
|
+
test('coalesces concurrent Protocol V2 runtime-context negotiation', async () => {
|
|
47
|
+
let resolveProtocolInfo:
|
|
48
|
+
| ((value: { message: typeof protocolV2ApplicationInfo }) => void)
|
|
49
|
+
| undefined;
|
|
50
|
+
const typedCall = jest.fn(
|
|
51
|
+
() =>
|
|
52
|
+
new Promise<{ message: typeof protocolV2ApplicationInfo }>(resolve => {
|
|
53
|
+
resolveProtocolInfo = resolve;
|
|
54
|
+
})
|
|
55
|
+
);
|
|
56
|
+
const device = createV2Device(typedCall);
|
|
57
|
+
|
|
58
|
+
const first = device.ensureProtocolV2RuntimeContext();
|
|
59
|
+
const second = device.ensureProtocolV2RuntimeContext();
|
|
60
|
+
resolveProtocolInfo?.({ message: protocolV2ApplicationInfo });
|
|
61
|
+
|
|
62
|
+
await expect(Promise.all([first, second])).resolves.toEqual([
|
|
63
|
+
protocolV2ApplicationInfo,
|
|
64
|
+
protocolV2ApplicationInfo,
|
|
65
|
+
]);
|
|
66
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
67
|
+
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
68
|
+
eventless_wallet_session: true,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('renegotiates Protocol V2 runtime context after transport disconnect', async () => {
|
|
73
|
+
const typedCall = jest.fn().mockResolvedValue({ message: protocolV2ApplicationInfo });
|
|
74
|
+
const device = createV2Device(typedCall);
|
|
75
|
+
|
|
76
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
77
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
78
|
+
device.markTransportDisconnected();
|
|
79
|
+
await device.ensureProtocolV2RuntimeContext();
|
|
80
|
+
|
|
81
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('rejects every waiter when runtime-context negotiation is invalidated', async () => {
|
|
85
|
+
let resolveProtocolInfo:
|
|
86
|
+
| ((value: { message: typeof protocolV2ApplicationInfo }) => void)
|
|
87
|
+
| undefined;
|
|
88
|
+
const typedCall = jest.fn(
|
|
89
|
+
() =>
|
|
90
|
+
new Promise<{ message: typeof protocolV2ApplicationInfo }>(resolve => {
|
|
91
|
+
resolveProtocolInfo = resolve;
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
const device = createV2Device(typedCall);
|
|
95
|
+
|
|
96
|
+
const first = device.ensureProtocolV2RuntimeContext();
|
|
97
|
+
const second = device.ensureProtocolV2RuntimeContext();
|
|
98
|
+
const firstExpectation = expect(first).rejects.toMatchObject({
|
|
99
|
+
errorCode: HardwareErrorCode.DeviceInitializeFailed,
|
|
100
|
+
});
|
|
101
|
+
const secondExpectation = expect(second).rejects.toMatchObject({
|
|
102
|
+
errorCode: HardwareErrorCode.DeviceInitializeFailed,
|
|
103
|
+
});
|
|
104
|
+
device.markTransportDisconnected();
|
|
105
|
+
resolveProtocolInfo?.({ message: protocolV2ApplicationInfo });
|
|
106
|
+
|
|
107
|
+
await Promise.all([firstExpectation, secondExpectation]);
|
|
108
|
+
});
|
|
109
|
+
|
|
46
110
|
test('does not expose the internal wallet session', async () => {
|
|
47
111
|
const device = createV2Device(jest.fn());
|
|
48
112
|
device.updateState({ protocol: 'V2' }, 'initialize');
|
|
@@ -159,15 +223,20 @@ describe('getDeviceState', () => {
|
|
|
159
223
|
throw new Error(`Unexpected request: ${requestType}`);
|
|
160
224
|
});
|
|
161
225
|
const device = createV2Device(typedCall);
|
|
162
|
-
device.updateState(
|
|
226
|
+
device.updateState(
|
|
227
|
+
{
|
|
228
|
+
protocol: 'V2',
|
|
229
|
+
identity: { deviceType: EDeviceType.Pro2 },
|
|
230
|
+
status: { mode },
|
|
231
|
+
raw: { protocolV2ProtocolInfo: getProtocolV2LoaderInfo(mode) },
|
|
232
|
+
},
|
|
233
|
+
'initialize'
|
|
234
|
+
);
|
|
163
235
|
|
|
164
236
|
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
165
237
|
|
|
166
238
|
expect(state.status.mode).toBe(mode);
|
|
167
|
-
expect(typedCall.
|
|
168
|
-
'DeviceInfoGet',
|
|
169
|
-
'ProtocolInfoRequest',
|
|
170
|
-
]);
|
|
239
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
171
240
|
}
|
|
172
241
|
);
|
|
173
242
|
|
|
@@ -187,7 +256,14 @@ describe('getDeviceState', () => {
|
|
|
187
256
|
return { message: { init_states: true, unlocked: true, device_id: 'device-1' } };
|
|
188
257
|
});
|
|
189
258
|
const device = createV2Device(typedCall);
|
|
190
|
-
device.updateState(
|
|
259
|
+
device.updateState(
|
|
260
|
+
{
|
|
261
|
+
protocol: 'V2',
|
|
262
|
+
status: { mode: 'normal' },
|
|
263
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
264
|
+
},
|
|
265
|
+
'initialize'
|
|
266
|
+
);
|
|
191
267
|
|
|
192
268
|
const state = await device.getDeviceState({ refreshSections: ['status'] });
|
|
193
269
|
|
|
@@ -205,7 +281,14 @@ describe('getDeviceState', () => {
|
|
|
205
281
|
},
|
|
206
282
|
});
|
|
207
283
|
const device = createV2Device(typedCall);
|
|
208
|
-
device.updateState(
|
|
284
|
+
device.updateState(
|
|
285
|
+
{
|
|
286
|
+
protocol: 'V2',
|
|
287
|
+
status: { mode: 'normal' },
|
|
288
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
289
|
+
},
|
|
290
|
+
'initialize'
|
|
291
|
+
);
|
|
209
292
|
const onState = jest.fn();
|
|
210
293
|
device.on(DEVICE.STATE, onState);
|
|
211
294
|
|
|
@@ -250,13 +333,18 @@ describe('getDeviceState', () => {
|
|
|
250
333
|
throw new Error(`Unexpected request: ${requestType}`);
|
|
251
334
|
});
|
|
252
335
|
const device = createV2Device(typedCall);
|
|
253
|
-
device.updateState(
|
|
336
|
+
device.updateState(
|
|
337
|
+
{
|
|
338
|
+
protocol: 'V2',
|
|
339
|
+
status: { mode: 'normal' },
|
|
340
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
341
|
+
},
|
|
342
|
+
'initialize'
|
|
343
|
+
);
|
|
254
344
|
|
|
255
345
|
const state = await device.getDeviceState({ refreshSections: ['status', 'settings'] });
|
|
256
346
|
|
|
257
347
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
258
|
-
'DeviceInfoGet',
|
|
259
|
-
'ProtocolInfoRequest',
|
|
260
348
|
'DeviceStatusGet',
|
|
261
349
|
'DeviceSettingsGet',
|
|
262
350
|
]);
|
|
@@ -288,7 +376,11 @@ describe('getDeviceState', () => {
|
|
|
288
376
|
});
|
|
289
377
|
const device = createV2Device(typedCall);
|
|
290
378
|
device.updateState(
|
|
291
|
-
{
|
|
379
|
+
{
|
|
380
|
+
protocol: 'V2',
|
|
381
|
+
status: { mode: 'normal', unlocked: true },
|
|
382
|
+
raw: { protocolV2ProtocolInfo: protocolV2ApplicationInfo },
|
|
383
|
+
},
|
|
292
384
|
'initialize'
|
|
293
385
|
);
|
|
294
386
|
|
|
@@ -374,6 +466,34 @@ describe('getDeviceState', () => {
|
|
|
374
466
|
});
|
|
375
467
|
});
|
|
376
468
|
|
|
469
|
+
test.each([
|
|
470
|
+
EDeviceType.Classic,
|
|
471
|
+
EDeviceType.Classic1s,
|
|
472
|
+
EDeviceType.ClassicPure,
|
|
473
|
+
EDeviceType.Mini,
|
|
474
|
+
])('keeps the GetFeatures-only firmware path for %s', async deviceType => {
|
|
475
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
476
|
+
message: {
|
|
477
|
+
onekey_device_type: deviceType.toUpperCase(),
|
|
478
|
+
initialized: true,
|
|
479
|
+
onekey_firmware_version: '3.5.0',
|
|
480
|
+
se_ver: '1.1.0.2',
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
const device = createV1Device(typedCall);
|
|
484
|
+
|
|
485
|
+
const state = await device.getDeviceState({
|
|
486
|
+
refreshSections: ['identity', 'versions', 'verification'],
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual(['GetFeatures']);
|
|
490
|
+
expect(state.versions).toMatchObject({
|
|
491
|
+
firmware: '3.5.0',
|
|
492
|
+
se: '1.1.0.2',
|
|
493
|
+
se01: '1.1.0.2',
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
377
497
|
test('does not clear Protocol V1 firmware details during a later runtime refresh', async () => {
|
|
378
498
|
let getFeaturesCount = 0;
|
|
379
499
|
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
|
-
import { getHomeScreenSize } from '../src/utils/homescreen';
|
|
3
|
+
import { getHomeScreenSize, getNftSize } from '../src/utils/homescreen';
|
|
4
4
|
|
|
5
5
|
describe('getHomeScreenSize', () => {
|
|
6
6
|
it('returns the Pro 2 wallpaper dimensions', () => {
|
|
@@ -21,4 +21,44 @@ describe('getHomeScreenSize', () => {
|
|
|
21
21
|
})
|
|
22
22
|
).toBeUndefined();
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
it('does not reuse the wallpaper homeScreenType for Pro 2 NFT dimensions', () => {
|
|
26
|
+
expect(
|
|
27
|
+
getHomeScreenSize({
|
|
28
|
+
deviceType: EDeviceType.Pro2,
|
|
29
|
+
homeScreenType: 'Nft',
|
|
30
|
+
thumbnail: false,
|
|
31
|
+
})
|
|
32
|
+
).toEqual({ width: 604, height: 1024 });
|
|
33
|
+
expect(
|
|
34
|
+
getHomeScreenSize({
|
|
35
|
+
deviceType: EDeviceType.Pro2,
|
|
36
|
+
homeScreenType: 'Nft',
|
|
37
|
+
thumbnail: true,
|
|
38
|
+
})
|
|
39
|
+
).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('getNftSize', () => {
|
|
44
|
+
it('returns the Pro 2 NFT image and thumbnail dimensions independently', () => {
|
|
45
|
+
expect(getNftSize({ deviceType: EDeviceType.Pro2 })).toEqual({ width: 540, height: 540 });
|
|
46
|
+
expect(getNftSize({ deviceType: EDeviceType.Pro2, thumbnail: true })).toEqual({
|
|
47
|
+
width: 263,
|
|
48
|
+
height: 263,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('preserves the legacy NFT dimensions', () => {
|
|
53
|
+
expect(getNftSize({ deviceType: EDeviceType.Touch })).toEqual({ width: 480, height: 800 });
|
|
54
|
+
expect(getNftSize({ deviceType: EDeviceType.Touch, thumbnail: true })).toEqual({
|
|
55
|
+
width: 238,
|
|
56
|
+
height: 238,
|
|
57
|
+
});
|
|
58
|
+
expect(getNftSize({ deviceType: EDeviceType.Pro, thumbnail: true })).toEqual({
|
|
59
|
+
width: 226,
|
|
60
|
+
height: 226,
|
|
61
|
+
radius: 40,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
24
64
|
});
|