@onekeyfe/hd-core 1.2.2-alpha.9 → 1.2.2
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/README.md +1 -1
- package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +503 -10
- package/__tests__/DeviceCommands.test.ts +254 -1
- package/__tests__/core-error-output.test.ts +169 -1
- package/__tests__/device-lifecycle-events.test.ts +395 -15
- package/__tests__/logBlockEvent.test.ts +45 -122
- package/__tests__/open-wallet-session-error-response.test.ts +2 -2
- package/__tests__/open-wallet-session.test.ts +8 -358
- package/__tests__/protocol-v2.test.ts +130 -0
- package/__tests__/public-device-state-api.test.ts +2 -7
- package/__tests__/search-devices.test.ts +196 -8
- package/__tests__/sol-sign-offchain-message.test.ts +64 -0
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetFeatures.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 +2 -15
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -0
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddress.d.ts +2 -0
- package/dist/api/allnetwork/AllNetworkGetAddress.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +7 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts.map +1 -1
- package/dist/api/device/DeviceVerify.d.ts.map +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +1 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +1 -1
- package/dist/data-manager/TransportManager.d.ts +2 -0
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -0
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +5 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +35 -30
- package/dist/index.js +587 -314
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/getFeatures.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -10
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -4
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/solSignOffchainMessage.d.ts +1 -0
- package/dist/types/api/solSignOffchainMessage.d.ts.map +1 -1
- package/dist/types/params.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +9 -4
- package/src/api/GetFeatures.ts +1 -0
- package/src/api/GetPassphraseState.ts +1 -0
- package/src/api/OpenWalletSession.ts +7 -77
- package/src/api/SearchDevices.ts +121 -27
- package/src/api/UploadPortfolio.ts +5 -4
- package/src/api/allnetwork/AllNetworkGetAddress.ts +79 -45
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +95 -24
- package/src/api/allnetwork/AllNetworkGetAddressByLoop.ts +3 -0
- package/src/api/device/DeviceVerify.ts +8 -0
- package/src/api/solana/SolSignOffchainMessage.ts +44 -4
- package/src/core/RequestQueue.ts +20 -0
- package/src/core/index.ts +159 -52
- package/src/data/messages/messages-protocol-v2.json +49 -33
- package/src/data/messages/messages.json +8 -5
- package/src/data-manager/TransportManager.ts +14 -3
- package/src/device/Device.ts +58 -27
- package/src/device/DeviceCommands.ts +29 -2
- package/src/events/logBlockEvent.ts +6 -75
- package/src/protocols/protocol-v2/walletSession.ts +14 -2
- package/src/types/api/getFeatures.ts +2 -1
- package/src/types/api/getPassphraseState.ts +2 -5
- package/src/types/api/openWalletSession.ts +7 -19
- package/src/types/api/protocolV2.ts +3 -4
- package/src/types/api/solSignOffchainMessage.ts +2 -0
- package/src/types/params.ts +7 -0
|
@@ -17,7 +17,7 @@ jest.mock('../src/data-manager/TransportManager', () => ({
|
|
|
17
17
|
}));
|
|
18
18
|
|
|
19
19
|
describe('openWalletSession error response', () => {
|
|
20
|
-
test('returns a structured invalid-parameter response for
|
|
20
|
+
test('returns a structured invalid-parameter response for the removed resume-hidden mode', async () => {
|
|
21
21
|
const core = initCore();
|
|
22
22
|
|
|
23
23
|
const response = await core.handleMessage({
|
|
@@ -34,7 +34,7 @@ describe('openWalletSession error response', () => {
|
|
|
34
34
|
expect(response).toMatchObject({
|
|
35
35
|
success: false,
|
|
36
36
|
payload: {
|
|
37
|
-
error: '
|
|
37
|
+
error: 'Parameter [mode] must be one of standard or select-hidden.',
|
|
38
38
|
code: HardwareErrorCode.CallMethodInvalidParameter,
|
|
39
39
|
},
|
|
40
40
|
});
|
|
@@ -982,45 +982,6 @@ describe('openWalletSession', () => {
|
|
|
982
982
|
);
|
|
983
983
|
});
|
|
984
984
|
|
|
985
|
-
test('uses an explicit wallet binding to resume on Protocol V2', async () => {
|
|
986
|
-
const typedCall = jest
|
|
987
|
-
.fn()
|
|
988
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
989
|
-
.mockResolvedValueOnce({
|
|
990
|
-
message: {
|
|
991
|
-
btc_test_address: 'hidden-state',
|
|
992
|
-
session_id: 'renewed-session',
|
|
993
|
-
},
|
|
994
|
-
});
|
|
995
|
-
const promptPassphrase = jest.fn();
|
|
996
|
-
const method = new OpenWalletSession({
|
|
997
|
-
payload: {
|
|
998
|
-
method: 'openWalletSession',
|
|
999
|
-
connectId: 'connect-id',
|
|
1000
|
-
mode: 'resume-hidden',
|
|
1001
|
-
deviceId: 'device-1',
|
|
1002
|
-
passphraseState: 'hidden-state',
|
|
1003
|
-
},
|
|
1004
|
-
});
|
|
1005
|
-
method.init();
|
|
1006
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
|
|
1007
|
-
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
1008
|
-
|
|
1009
|
-
await expect(method.run()).resolves.toEqual({
|
|
1010
|
-
protocol: 'V2',
|
|
1011
|
-
walletType: 'hidden',
|
|
1012
|
-
deviceId: 'device-1',
|
|
1013
|
-
passphraseState: 'hidden-state',
|
|
1014
|
-
resumed: true,
|
|
1015
|
-
});
|
|
1016
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1017
|
-
session_id: 'known-session',
|
|
1018
|
-
btc_test_address: 'hidden-state',
|
|
1019
|
-
...standardSessionGet,
|
|
1020
|
-
});
|
|
1021
|
-
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
985
|
test.each([
|
|
1025
986
|
{
|
|
1026
987
|
mode: 'standard',
|
|
@@ -1035,12 +996,6 @@ describe('openWalletSession', () => {
|
|
|
1035
996
|
deviceId: 'device-1',
|
|
1036
997
|
passphraseState: 'hidden-state',
|
|
1037
998
|
},
|
|
1038
|
-
{
|
|
1039
|
-
mode: 'resume-hidden',
|
|
1040
|
-
deviceId: 'device-1',
|
|
1041
|
-
passphraseState: 'hidden-state',
|
|
1042
|
-
initSession: false,
|
|
1043
|
-
},
|
|
1044
999
|
])('rejects legacy parameters combined with explicit mode: %o', params => {
|
|
1045
1000
|
const method = new OpenWalletSession({
|
|
1046
1001
|
payload: {
|
|
@@ -1076,63 +1031,7 @@ describe('openWalletSession', () => {
|
|
|
1076
1031
|
}
|
|
1077
1032
|
);
|
|
1078
1033
|
|
|
1079
|
-
test('
|
|
1080
|
-
const typedCall = jest.fn().mockResolvedValue({
|
|
1081
|
-
type: 'PassphraseState',
|
|
1082
|
-
message: {
|
|
1083
|
-
passphrase_state: 'hidden-state',
|
|
1084
|
-
session_id: 'known-session',
|
|
1085
|
-
unlocked_attach_pin: false,
|
|
1086
|
-
},
|
|
1087
|
-
});
|
|
1088
|
-
const method = new OpenWalletSession({
|
|
1089
|
-
payload: {
|
|
1090
|
-
method: 'openWalletSession',
|
|
1091
|
-
connectId: 'connect-id',
|
|
1092
|
-
mode: 'resume-hidden',
|
|
1093
|
-
deviceId: 'device-1',
|
|
1094
|
-
passphraseState: 'hidden-state',
|
|
1095
|
-
},
|
|
1096
|
-
});
|
|
1097
|
-
method.init();
|
|
1098
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
|
|
1099
|
-
const device = createDevice({ typedCall });
|
|
1100
|
-
device.isProtocolV2 = () => false;
|
|
1101
|
-
device.features = {
|
|
1102
|
-
...device.features,
|
|
1103
|
-
deviceId: 'device-1',
|
|
1104
|
-
unlocked: true,
|
|
1105
|
-
sessionId: null,
|
|
1106
|
-
};
|
|
1107
|
-
device.getCurrentFirmwareVersionString = () => '4.15.0';
|
|
1108
|
-
device.getCurrentDeviceType = () => EDeviceType.Pro;
|
|
1109
|
-
device.getFeatures = jest.fn();
|
|
1110
|
-
method.device = device as any;
|
|
1111
|
-
|
|
1112
|
-
await expect(method.run()).resolves.toMatchObject({
|
|
1113
|
-
protocol: 'V1',
|
|
1114
|
-
walletType: 'hidden',
|
|
1115
|
-
passphraseState: 'hidden-state',
|
|
1116
|
-
resumed: true,
|
|
1117
|
-
});
|
|
1118
|
-
expect(device.initialize).toHaveBeenCalledWith({
|
|
1119
|
-
deviceId: 'device-1',
|
|
1120
|
-
passphraseState: 'hidden-state',
|
|
1121
|
-
});
|
|
1122
|
-
expect(typedCall).toHaveBeenCalledWith('GetPassphraseState', 'PassphraseState', {
|
|
1123
|
-
passphrase_state: 'hidden-state',
|
|
1124
|
-
});
|
|
1125
|
-
});
|
|
1126
|
-
|
|
1127
|
-
test('rejects a Protocol V1 resume when the device returns another hidden wallet', async () => {
|
|
1128
|
-
const typedCall = jest.fn().mockResolvedValue({
|
|
1129
|
-
type: 'PassphraseState',
|
|
1130
|
-
message: {
|
|
1131
|
-
passphrase_state: 'other-hidden-state',
|
|
1132
|
-
session_id: 'other-session',
|
|
1133
|
-
unlocked_attach_pin: false,
|
|
1134
|
-
},
|
|
1135
|
-
});
|
|
1034
|
+
test('rejects the removed resume-hidden mode', () => {
|
|
1136
1035
|
const method = new OpenWalletSession({
|
|
1137
1036
|
payload: {
|
|
1138
1037
|
method: 'openWalletSession',
|
|
@@ -1140,28 +1039,15 @@ describe('openWalletSession', () => {
|
|
|
1140
1039
|
mode: 'resume-hidden',
|
|
1141
1040
|
deviceId: 'device-1',
|
|
1142
1041
|
passphraseState: 'hidden-state',
|
|
1143
|
-
},
|
|
1042
|
+
} as any,
|
|
1144
1043
|
});
|
|
1145
|
-
method.init();
|
|
1146
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
|
|
1147
|
-
const device = createDevice({ typedCall });
|
|
1148
|
-
device.isProtocolV2 = () => false;
|
|
1149
|
-
device.features = {
|
|
1150
|
-
...device.features,
|
|
1151
|
-
deviceId: 'device-1',
|
|
1152
|
-
unlocked: true,
|
|
1153
|
-
sessionId: null,
|
|
1154
|
-
};
|
|
1155
|
-
device.getCurrentFirmwareVersionString = () => '4.15.0';
|
|
1156
|
-
device.getCurrentDeviceType = () => EDeviceType.Pro;
|
|
1157
|
-
device.getFeatures = jest.fn();
|
|
1158
|
-
method.device = device as any;
|
|
1159
1044
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1045
|
+
expect(() => method.init()).toThrow(
|
|
1046
|
+
expect.objectContaining({
|
|
1047
|
+
errorCode: HardwareErrorCode.CallMethodInvalidParameter,
|
|
1048
|
+
message: 'Parameter [mode] must be one of standard or select-hidden.',
|
|
1049
|
+
})
|
|
1050
|
+
);
|
|
1165
1051
|
});
|
|
1166
1052
|
|
|
1167
1053
|
test('opens the standard wallet without selecting a hidden wallet', async () => {
|
|
@@ -1882,242 +1768,6 @@ describe('openWalletSession', () => {
|
|
|
1882
1768
|
}
|
|
1883
1769
|
);
|
|
1884
1770
|
|
|
1885
|
-
test('resumes a known hidden wallet without prompting or device selection', async () => {
|
|
1886
|
-
const typedCall = jest
|
|
1887
|
-
.fn()
|
|
1888
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1889
|
-
.mockResolvedValueOnce({
|
|
1890
|
-
message: {
|
|
1891
|
-
btc_test_address: 'hidden-state',
|
|
1892
|
-
session_id: 'renewed-session',
|
|
1893
|
-
},
|
|
1894
|
-
});
|
|
1895
|
-
const promptPassphrase = jest.fn();
|
|
1896
|
-
const method = new OpenWalletSession({
|
|
1897
|
-
payload: {
|
|
1898
|
-
method: 'openWalletSession',
|
|
1899
|
-
connectId: 'connect-id',
|
|
1900
|
-
mode: 'resume-hidden',
|
|
1901
|
-
deviceId: 'device-1',
|
|
1902
|
-
passphraseState: 'hidden-state',
|
|
1903
|
-
},
|
|
1904
|
-
});
|
|
1905
|
-
method.init();
|
|
1906
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
|
|
1907
|
-
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
1908
|
-
|
|
1909
|
-
await expect(method.run()).resolves.toEqual({
|
|
1910
|
-
protocol: 'V2',
|
|
1911
|
-
walletType: 'hidden',
|
|
1912
|
-
deviceId: 'device-1',
|
|
1913
|
-
passphraseState: 'hidden-state',
|
|
1914
|
-
resumed: true,
|
|
1915
|
-
});
|
|
1916
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1917
|
-
session_id: 'known-session',
|
|
1918
|
-
btc_test_address: 'hidden-state',
|
|
1919
|
-
...standardSessionGet,
|
|
1920
|
-
});
|
|
1921
|
-
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
1922
|
-
});
|
|
1923
|
-
|
|
1924
|
-
test('validates a locked Protocol V2 resume binding after deviceId refresh', async () => {
|
|
1925
|
-
const typedCall = jest
|
|
1926
|
-
.fn()
|
|
1927
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
1928
|
-
.mockResolvedValueOnce({
|
|
1929
|
-
message: {
|
|
1930
|
-
btc_test_address: 'hidden-state',
|
|
1931
|
-
session_id: 'renewed-session',
|
|
1932
|
-
},
|
|
1933
|
-
});
|
|
1934
|
-
const method = new OpenWalletSession({
|
|
1935
|
-
payload: {
|
|
1936
|
-
method: 'openWalletSession',
|
|
1937
|
-
connectId: 'connect-id',
|
|
1938
|
-
mode: 'resume-hidden',
|
|
1939
|
-
deviceId: 'device-1',
|
|
1940
|
-
passphraseState: 'hidden-state',
|
|
1941
|
-
},
|
|
1942
|
-
});
|
|
1943
|
-
method.init();
|
|
1944
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
|
|
1945
|
-
const device = createDevice({ typedCall });
|
|
1946
|
-
device.features.unlocked = false;
|
|
1947
|
-
device.getDeviceState = jest
|
|
1948
|
-
.fn()
|
|
1949
|
-
.mockResolvedValueOnce({
|
|
1950
|
-
identity: { deviceId: undefined },
|
|
1951
|
-
status: { unlocked: false, passphraseProtection: true },
|
|
1952
|
-
})
|
|
1953
|
-
.mockResolvedValueOnce({
|
|
1954
|
-
identity: { deviceId: 'device-1' },
|
|
1955
|
-
status: {
|
|
1956
|
-
unlocked: true,
|
|
1957
|
-
passphraseProtection: true,
|
|
1958
|
-
unlockedAttachPin: false,
|
|
1959
|
-
},
|
|
1960
|
-
});
|
|
1961
|
-
device.unlockDevice = jest.fn().mockImplementation(() => {
|
|
1962
|
-
device.features.unlocked = true;
|
|
1963
|
-
return Promise.resolve(device.features);
|
|
1964
|
-
});
|
|
1965
|
-
method.device = device as any;
|
|
1966
|
-
|
|
1967
|
-
await expect(method.run()).resolves.toMatchObject({
|
|
1968
|
-
protocol: 'V2',
|
|
1969
|
-
walletType: 'hidden',
|
|
1970
|
-
deviceId: 'device-1',
|
|
1971
|
-
passphraseState: 'hidden-state',
|
|
1972
|
-
resumed: true,
|
|
1973
|
-
});
|
|
1974
|
-
expect(device.unlockDevice).toHaveBeenCalledTimes(1);
|
|
1975
|
-
expect(device.getDeviceState).toHaveBeenCalledTimes(2);
|
|
1976
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
1977
|
-
session_id: 'known-session',
|
|
1978
|
-
btc_test_address: 'hidden-state',
|
|
1979
|
-
...standardSessionGet,
|
|
1980
|
-
});
|
|
1981
|
-
const sessionGetCall = typedCall.mock.calls.findIndex(
|
|
1982
|
-
([requestName]) => requestName === 'DeviceSessionGet'
|
|
1983
|
-
);
|
|
1984
|
-
expect(device.getDeviceState.mock.invocationCallOrder[1]).toBeLessThan(
|
|
1985
|
-
typedCall.mock.invocationCallOrder[sessionGetCall]
|
|
1986
|
-
);
|
|
1987
|
-
});
|
|
1988
|
-
|
|
1989
|
-
test('rejects a Protocol V2 resume when the refreshed deviceId does not match', async () => {
|
|
1990
|
-
const typedCall = jest.fn().mockResolvedValue({
|
|
1991
|
-
message: {
|
|
1992
|
-
btc_test_address: 'hidden-state',
|
|
1993
|
-
session_id: 'renewed-session',
|
|
1994
|
-
},
|
|
1995
|
-
});
|
|
1996
|
-
const method = new OpenWalletSession({
|
|
1997
|
-
payload: {
|
|
1998
|
-
method: 'openWalletSession',
|
|
1999
|
-
connectId: 'connect-id',
|
|
2000
|
-
mode: 'resume-hidden',
|
|
2001
|
-
deviceId: 'device-1',
|
|
2002
|
-
passphraseState: 'hidden-state',
|
|
2003
|
-
},
|
|
2004
|
-
});
|
|
2005
|
-
method.init();
|
|
2006
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
|
|
2007
|
-
const device = createDevice({ typedCall });
|
|
2008
|
-
device.features.unlocked = false;
|
|
2009
|
-
device.getDeviceState = jest
|
|
2010
|
-
.fn()
|
|
2011
|
-
.mockResolvedValueOnce({
|
|
2012
|
-
identity: { deviceId: undefined },
|
|
2013
|
-
status: { passphraseProtection: true },
|
|
2014
|
-
})
|
|
2015
|
-
.mockResolvedValueOnce({
|
|
2016
|
-
identity: { deviceId: 'other-device' },
|
|
2017
|
-
status: {
|
|
2018
|
-
unlocked: true,
|
|
2019
|
-
passphraseProtection: true,
|
|
2020
|
-
unlockedAttachPin: false,
|
|
2021
|
-
},
|
|
2022
|
-
});
|
|
2023
|
-
device.unlockDevice = jest.fn().mockImplementation(() => {
|
|
2024
|
-
device.features.unlocked = true;
|
|
2025
|
-
return Promise.resolve(device.features);
|
|
2026
|
-
});
|
|
2027
|
-
method.device = device as any;
|
|
2028
|
-
|
|
2029
|
-
await expect(method.run()).rejects.toMatchObject({
|
|
2030
|
-
errorCode: HardwareErrorCode.DeviceCheckDeviceIdError,
|
|
2031
|
-
});
|
|
2032
|
-
expect(typedCall).not.toHaveBeenCalled();
|
|
2033
|
-
expect(device.clearInternalState).not.toHaveBeenCalled();
|
|
2034
|
-
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBeUndefined();
|
|
2035
|
-
});
|
|
2036
|
-
|
|
2037
|
-
test('reselects the expected hidden wallet when a cached Protocol V2 session is invalid', async () => {
|
|
2038
|
-
const typedCall = jest
|
|
2039
|
-
.fn()
|
|
2040
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
2041
|
-
.mockRejectedValueOnce(
|
|
2042
|
-
Object.assign(new Error('Invalid session'), {
|
|
2043
|
-
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
2044
|
-
})
|
|
2045
|
-
)
|
|
2046
|
-
.mockResolvedValueOnce({ message: {} })
|
|
2047
|
-
.mockResolvedValueOnce({
|
|
2048
|
-
message: {
|
|
2049
|
-
btc_test_address: 'hidden-state',
|
|
2050
|
-
session_id: 'renewed-session',
|
|
2051
|
-
},
|
|
2052
|
-
});
|
|
2053
|
-
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
2054
|
-
const method = new OpenWalletSession({
|
|
2055
|
-
payload: {
|
|
2056
|
-
method: 'openWalletSession',
|
|
2057
|
-
connectId: 'connect-id',
|
|
2058
|
-
mode: 'resume-hidden',
|
|
2059
|
-
deviceId: 'device-1',
|
|
2060
|
-
passphraseState: 'hidden-state',
|
|
2061
|
-
},
|
|
2062
|
-
});
|
|
2063
|
-
method.init();
|
|
2064
|
-
deviceWalletSessionStore.set('device-1', 'hidden-state', 'expired-session');
|
|
2065
|
-
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
2066
|
-
|
|
2067
|
-
await expect(method.run()).resolves.toEqual({
|
|
2068
|
-
protocol: 'V2',
|
|
2069
|
-
walletType: 'hidden',
|
|
2070
|
-
deviceId: 'device-1',
|
|
2071
|
-
passphraseState: 'hidden-state',
|
|
2072
|
-
resumed: false,
|
|
2073
|
-
});
|
|
2074
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
2075
|
-
session_id: 'expired-session',
|
|
2076
|
-
btc_test_address: 'hidden-state',
|
|
2077
|
-
...standardSessionGet,
|
|
2078
|
-
});
|
|
2079
|
-
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
2080
|
-
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
|
|
2081
|
-
});
|
|
2082
|
-
|
|
2083
|
-
test('selects the expected hidden wallet when Protocol V2 has no cached session', async () => {
|
|
2084
|
-
const typedCall = jest
|
|
2085
|
-
.fn()
|
|
2086
|
-
.mockResolvedValueOnce({ message: { version: 2 } })
|
|
2087
|
-
.mockResolvedValueOnce({
|
|
2088
|
-
message: {
|
|
2089
|
-
btc_test_address: 'hidden-state',
|
|
2090
|
-
session_id: 'new-session',
|
|
2091
|
-
},
|
|
2092
|
-
});
|
|
2093
|
-
const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
|
|
2094
|
-
const method = new OpenWalletSession({
|
|
2095
|
-
payload: {
|
|
2096
|
-
method: 'openWalletSession',
|
|
2097
|
-
connectId: 'connect-id',
|
|
2098
|
-
mode: 'resume-hidden',
|
|
2099
|
-
deviceId: 'device-1',
|
|
2100
|
-
passphraseState: 'hidden-state',
|
|
2101
|
-
},
|
|
2102
|
-
});
|
|
2103
|
-
method.init();
|
|
2104
|
-
method.device = createDevice({ typedCall, promptPassphrase }) as any;
|
|
2105
|
-
|
|
2106
|
-
await expect(method.run()).resolves.toEqual({
|
|
2107
|
-
protocol: 'V2',
|
|
2108
|
-
walletType: 'hidden',
|
|
2109
|
-
deviceId: 'device-1',
|
|
2110
|
-
passphraseState: 'hidden-state',
|
|
2111
|
-
resumed: false,
|
|
2112
|
-
});
|
|
2113
|
-
expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
|
|
2114
|
-
btc_test_address: 'hidden-state',
|
|
2115
|
-
...standardSessionGet,
|
|
2116
|
-
});
|
|
2117
|
-
expect(promptPassphrase).not.toHaveBeenCalled();
|
|
2118
|
-
expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
|
|
2119
|
-
});
|
|
2120
|
-
|
|
2121
1771
|
test.each([false, true])(
|
|
2122
1772
|
'openWalletSession does not thread CommonParams.deriveCardano: %s',
|
|
2123
1773
|
async deriveCardano => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
2
3
|
import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
4
|
import { sha256 } from '@noble/hashes/sha256';
|
|
4
5
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
@@ -536,6 +537,41 @@ describe('UploadPortfolio', () => {
|
|
|
536
537
|
});
|
|
537
538
|
});
|
|
538
539
|
|
|
540
|
+
test('emits transfer progress when progress UI is requested', async () => {
|
|
541
|
+
const typedCall = jest
|
|
542
|
+
.fn()
|
|
543
|
+
.mockResolvedValueOnce({ message: { processed_byte: 3 } })
|
|
544
|
+
.mockResolvedValueOnce({ message: { message: 'Portfolio updated' } });
|
|
545
|
+
const method = new UploadPortfolio({
|
|
546
|
+
id: 1,
|
|
547
|
+
payload: {
|
|
548
|
+
method: 'uploadPortfolio',
|
|
549
|
+
packageBase64: 'AQID',
|
|
550
|
+
uiMode: 'progress',
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
(method as any).device = stubPortfolioDevice({ commands: { typedCall } });
|
|
554
|
+
method.postMessage = jest.fn();
|
|
555
|
+
|
|
556
|
+
method.init();
|
|
557
|
+
await method.run();
|
|
558
|
+
|
|
559
|
+
expect(method.unlockPolicy).toBe('none');
|
|
560
|
+
expect(method.protocolV2UiMode).toBe('auto');
|
|
561
|
+
expect(method.protocolV2UiInteraction).toBeUndefined();
|
|
562
|
+
expect(method.payload.emitProgress).toBe(true);
|
|
563
|
+
expect(method.postMessage).toHaveBeenCalledWith({
|
|
564
|
+
event: 'UI_EVENT',
|
|
565
|
+
type: UI_REQUEST.DEVICE_PROGRESS,
|
|
566
|
+
payload: expect.objectContaining({
|
|
567
|
+
progress: 100,
|
|
568
|
+
transferredBytes: 3,
|
|
569
|
+
totalBytes: 3,
|
|
570
|
+
elapsedMs: expect.any(Number),
|
|
571
|
+
}),
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
539
575
|
test('does not apply PortfolioUpdate when staging fails', async () => {
|
|
540
576
|
const typedCall = jest.fn().mockRejectedValue(new Error('write failed'));
|
|
541
577
|
const method = new UploadPortfolio({
|
|
@@ -1789,6 +1825,56 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1789
1825
|
]);
|
|
1790
1826
|
});
|
|
1791
1827
|
|
|
1828
|
+
test.each([false, true])(
|
|
1829
|
+
'rejects the first incorrect passphrase after a missing or expired session (cached: %s)',
|
|
1830
|
+
async cached => {
|
|
1831
|
+
const deviceId = `first-passphrase-error-${String(cached)}`;
|
|
1832
|
+
const device = Device.fromDescriptor({
|
|
1833
|
+
id: deviceId,
|
|
1834
|
+
path: deviceId,
|
|
1835
|
+
protocolType: 'V2',
|
|
1836
|
+
} as never);
|
|
1837
|
+
device.features = normalizeProtocolV2Features(
|
|
1838
|
+
{ ...descriptor, protocolType: 'V2' } as never,
|
|
1839
|
+
{
|
|
1840
|
+
status: { device_id: deviceId, unlocked: true, passphrase_enabled: true },
|
|
1841
|
+
}
|
|
1842
|
+
);
|
|
1843
|
+
device.passphraseState = 'expected-public-address';
|
|
1844
|
+
if (cached) {
|
|
1845
|
+
preloadSessionCache(deviceId, device.passphraseState, randomBytes(32).toString('hex'));
|
|
1846
|
+
}
|
|
1847
|
+
let reads = 0;
|
|
1848
|
+
const typedCall = createWalletSessionTypedCall(
|
|
1849
|
+
jest.fn((request: string) => {
|
|
1850
|
+
if (request === 'DeviceSessionAskPassphrase') return { message: {} };
|
|
1851
|
+
if (request === 'DeviceSessionGet') {
|
|
1852
|
+
reads += 1;
|
|
1853
|
+
if (reads === 1) {
|
|
1854
|
+
throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
|
|
1855
|
+
}
|
|
1856
|
+
return {
|
|
1857
|
+
message: {
|
|
1858
|
+
session_id: randomBytes(32).toString('hex'),
|
|
1859
|
+
btc_test_address: 'different-public-address',
|
|
1860
|
+
},
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
1864
|
+
})
|
|
1865
|
+
);
|
|
1866
|
+
const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
|
|
1867
|
+
device.commands = { typedCall, promptPassphrase } as never;
|
|
1868
|
+
|
|
1869
|
+
await expect(getProtocolV2WalletSession(device)).rejects.toMatchObject({
|
|
1870
|
+
errorCode: HardwareErrorCode.DeviceCheckPassphraseStateError,
|
|
1871
|
+
});
|
|
1872
|
+
expect(promptPassphrase).toHaveBeenCalledTimes(1);
|
|
1873
|
+
expect(reads).toBe(2);
|
|
1874
|
+
expect(device.getInternalState()).toBeUndefined();
|
|
1875
|
+
}
|
|
1876
|
+
);
|
|
1877
|
+
|
|
1792
1878
|
test('reselects a hidden wallet when the cached Pro2 session resolves to another wallet', async () => {
|
|
1793
1879
|
const device = Device.fromDescriptor({
|
|
1794
1880
|
id: 'cache-device-mismatch',
|
|
@@ -8892,6 +8978,50 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8892
8978
|
expect(recoverProtocolV2FileTransfer).not.toHaveBeenCalled();
|
|
8893
8979
|
});
|
|
8894
8980
|
|
|
8981
|
+
test('keeps public transfer bytes monotonic when a V4 file retry restarts at zero', async () => {
|
|
8982
|
+
const method = new FirmwareUpdateV4({
|
|
8983
|
+
id: 1,
|
|
8984
|
+
payload: {
|
|
8985
|
+
method: 'firmwareUpdateV4',
|
|
8986
|
+
},
|
|
8987
|
+
});
|
|
8988
|
+
let writeCount = 0;
|
|
8989
|
+
(method as any).fileWriteChunk = jest.fn(
|
|
8990
|
+
(_path: string, _size: number, offset: number, data: Uint8Array) => {
|
|
8991
|
+
writeCount += 1;
|
|
8992
|
+
if (writeCount === 3) {
|
|
8993
|
+
return Promise.reject(new Error('transport timeout'));
|
|
8994
|
+
}
|
|
8995
|
+
return Promise.resolve({
|
|
8996
|
+
message: { processed_byte: offset + data.byteLength },
|
|
8997
|
+
});
|
|
8998
|
+
}
|
|
8999
|
+
);
|
|
9000
|
+
(method as any).getProtocolV2FirmwareChunkSize = jest.fn().mockReturnValue(1000);
|
|
9001
|
+
(method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
|
|
9002
|
+
method.postProgressMessage = jest.fn();
|
|
9003
|
+
const source = await openFirmwareByteSource({
|
|
9004
|
+
binary: new Uint8Array(3000).buffer,
|
|
9005
|
+
});
|
|
9006
|
+
|
|
9007
|
+
try {
|
|
9008
|
+
await (method as any).protocolV2SourceUpdateProcess({
|
|
9009
|
+
source,
|
|
9010
|
+
filePath: 'vol1:firmware.bin',
|
|
9011
|
+
processedSize: 0,
|
|
9012
|
+
totalSize: 3000,
|
|
9013
|
+
});
|
|
9014
|
+
} finally {
|
|
9015
|
+
await source?.close();
|
|
9016
|
+
}
|
|
9017
|
+
|
|
9018
|
+
const transferredBytes = (method.postProgressMessage as jest.Mock).mock.calls.map(
|
|
9019
|
+
([, , metrics]) => metrics.transferredBytes
|
|
9020
|
+
);
|
|
9021
|
+
expect(transferredBytes).toEqual([1000, 2000, 3000]);
|
|
9022
|
+
expect((method as any).recoverProtocolV2FileTransfer).toHaveBeenCalledTimes(1);
|
|
9023
|
+
});
|
|
9024
|
+
|
|
8895
9025
|
test('throttles repeated transfer progress while preserving file completion', async () => {
|
|
8896
9026
|
const method = new FirmwareUpdateV4({
|
|
8897
9027
|
id: 1,
|
|
@@ -22,7 +22,6 @@ describe('public device state API boundary', () => {
|
|
|
22
22
|
expect((publicTypes as Record<string, unknown>).OpenWalletSessionMode).toEqual({
|
|
23
23
|
Standard: 'standard',
|
|
24
24
|
SelectHidden: 'select-hidden',
|
|
25
|
-
ResumeHidden: 'resume-hidden',
|
|
26
25
|
});
|
|
27
26
|
});
|
|
28
27
|
|
|
@@ -83,9 +82,7 @@ describe('public device state API boundary', () => {
|
|
|
83
82
|
useEmptyPassphrase: false,
|
|
84
83
|
});
|
|
85
84
|
await api.openWalletSession('device-2', {
|
|
86
|
-
mode: publicTypes.OpenWalletSessionMode.
|
|
87
|
-
deviceId: 'wallet-device-1',
|
|
88
|
-
passphraseState: 'wallet-state-1',
|
|
85
|
+
mode: publicTypes.OpenWalletSessionMode.SelectHidden,
|
|
89
86
|
});
|
|
90
87
|
await api.clearSessionCache({
|
|
91
88
|
deviceId: 'wallet-device-1',
|
|
@@ -100,9 +97,7 @@ describe('public device state API boundary', () => {
|
|
|
100
97
|
});
|
|
101
98
|
expect(call).toHaveBeenNthCalledWith(2, {
|
|
102
99
|
connectId: 'device-2',
|
|
103
|
-
mode: '
|
|
104
|
-
deviceId: 'wallet-device-1',
|
|
105
|
-
passphraseState: 'wallet-state-1',
|
|
100
|
+
mode: 'select-hidden',
|
|
106
101
|
method: 'openWalletSession',
|
|
107
102
|
});
|
|
108
103
|
expect(call).toHaveBeenNthCalledWith(3, {
|