@onekeyfe/hd-core 1.2.2-alpha.103 → 1.2.2-alpha.105
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 +153 -0
- package/__tests__/DeviceCommands.test.ts +254 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +3 -0
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +1 -0
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +92 -15
- 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/allnetwork/AllNetworkGetAddressBase.ts +56 -15
- package/src/api/allnetwork/AllNetworkGetAddressByLoop.ts +3 -0
- package/src/data/messages/messages-protocol-v2.json +21 -0
- package/src/device/DeviceCommands.ts +29 -2
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
1
3
|
import AllNetworkGetAddressBase from '../src/api/allnetwork/AllNetworkGetAddressBase';
|
|
2
4
|
import AllNetworkGetAddress from '../src/api/allnetwork/AllNetworkGetAddress';
|
|
5
|
+
import AllNetworkGetAddressByLoop from '../src/api/allnetwork/AllNetworkGetAddressByLoop';
|
|
3
6
|
import { findMethod } from '../src/api/utils';
|
|
4
7
|
import { getActiveRequestsByDeviceInstance } from '../src/utils/tracing';
|
|
5
8
|
|
|
@@ -18,6 +21,156 @@ class TestAllNetworkMethod extends AllNetworkGetAddressBase {
|
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
describe('Pro2 all-network loading lifecycle', () => {
|
|
25
|
+
function setup(callback = false) {
|
|
26
|
+
const Method = callback ? AllNetworkGetAddressByLoop : AllNetworkGetAddress;
|
|
27
|
+
const method = new Method({
|
|
28
|
+
id: 1,
|
|
29
|
+
payload: {
|
|
30
|
+
method: callback ? 'allNetworkGetAddressByLoop' : 'allNetworkGetAddress',
|
|
31
|
+
connectId: 'test-device',
|
|
32
|
+
callbackId: 'item',
|
|
33
|
+
callbackIdFinish: 'finish',
|
|
34
|
+
bundle: [
|
|
35
|
+
{ network: 'evm', path: "m/44'/60'/0'/0/0" },
|
|
36
|
+
{ network: 'sol', path: "m/44'/501'/0'" },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const calls: string[] = [];
|
|
41
|
+
const typedCall = jest.fn((type: string, _response: string, message: { action?: number }) => {
|
|
42
|
+
calls.push(type === 'DeviceAnimationControl' ? `animation:${message.action}` : type);
|
|
43
|
+
return Promise.resolve({ message: { root_fingerprint: 0 } });
|
|
44
|
+
});
|
|
45
|
+
method.device = {
|
|
46
|
+
commands: { typedCall, disposed: false },
|
|
47
|
+
isProtocolV2: jest.fn(() => true),
|
|
48
|
+
getCurrentDeviceType: jest.fn(() => EDeviceType.Pro2),
|
|
49
|
+
ensureProtocolV2RuntimeContext: jest.fn(() =>
|
|
50
|
+
Promise.resolve({ supported_messages: [60461] })
|
|
51
|
+
),
|
|
52
|
+
} as any;
|
|
53
|
+
method.postMessage = jest.fn();
|
|
54
|
+
method.context = {
|
|
55
|
+
registerCallbackTask: jest.fn(),
|
|
56
|
+
cancelCallbackTasks: jest.fn(() => method.device.pendingCallbackPromise?.resolve()),
|
|
57
|
+
} as any;
|
|
58
|
+
const callMethod = jest.spyOn(method, 'callMethod').mockImplementation(name => {
|
|
59
|
+
calls.push(name);
|
|
60
|
+
return Promise.resolve([{ success: true, payload: {} }] as any);
|
|
61
|
+
});
|
|
62
|
+
return { method, calls, typedCall, callMethod };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
test('keeps one session across fingerprint and all chain requests', async () => {
|
|
66
|
+
const { method, calls } = setup();
|
|
67
|
+
await method.run();
|
|
68
|
+
expect(calls).toEqual([
|
|
69
|
+
'animation:1',
|
|
70
|
+
'GetPublicKey',
|
|
71
|
+
'evmGetAddress',
|
|
72
|
+
'solGetAddress',
|
|
73
|
+
'animation:2',
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test.each(['V1', 'unsupported', 'Neo'])('does not change %s device behavior', async variant => {
|
|
78
|
+
const { method, calls } = setup();
|
|
79
|
+
if (variant === 'V1') jest.spyOn(method.device, 'isProtocolV2').mockReturnValue(false);
|
|
80
|
+
if (variant === 'Neo')
|
|
81
|
+
jest.spyOn(method.device, 'getCurrentDeviceType').mockReturnValue(EDeviceType.Neo);
|
|
82
|
+
if (variant === 'unsupported') {
|
|
83
|
+
jest
|
|
84
|
+
.spyOn(method.device, 'ensureProtocolV2RuntimeContext')
|
|
85
|
+
.mockResolvedValue({ supported_messages: [] } as any);
|
|
86
|
+
}
|
|
87
|
+
await method.run();
|
|
88
|
+
expect(calls).toEqual(['GetPublicKey', 'evmGetAddress', 'solGetAddress']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('does not reconnect for cleanup after chain failure', async () => {
|
|
92
|
+
const { method, calls, callMethod } = setup();
|
|
93
|
+
const error = new Error('chain failed');
|
|
94
|
+
callMethod.mockRejectedValue(error);
|
|
95
|
+
await expect(method.run()).rejects.toBe(error);
|
|
96
|
+
expect(calls).toEqual(['animation:1', 'GetPublicKey']);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('does not replace a successful result when Stop fails', async () => {
|
|
100
|
+
const { method, calls, typedCall } = setup();
|
|
101
|
+
typedCall.mockResolvedValueOnce({ message: { root_fingerprint: 0 } });
|
|
102
|
+
typedCall.mockResolvedValueOnce({ message: { root_fingerprint: 0 } });
|
|
103
|
+
typedCall.mockImplementationOnce(() => {
|
|
104
|
+
calls.push('stop-failed');
|
|
105
|
+
return Promise.reject(new Error('cleanup failed'));
|
|
106
|
+
});
|
|
107
|
+
await expect(method.run()).resolves.toHaveLength(2);
|
|
108
|
+
expect(calls).toEqual(['evmGetAddress', 'solGetAddress', 'stop-failed']);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('does not continue wallet commands when starting the session fails', async () => {
|
|
112
|
+
const { method, typedCall, callMethod } = setup();
|
|
113
|
+
const error = new Error('link disconnected');
|
|
114
|
+
typedCall.mockRejectedValueOnce(error);
|
|
115
|
+
await expect(method.run()).rejects.toBe(error);
|
|
116
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
117
|
+
expect(callMethod).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test.each(['success', 'failure', 'cancel'])(
|
|
121
|
+
'keeps callback loading until background %s',
|
|
122
|
+
async outcome => {
|
|
123
|
+
const { method, calls, callMethod } = setup(true);
|
|
124
|
+
let finishChain: () => void = () => {};
|
|
125
|
+
const chainPending = new Promise<void>(resolve => {
|
|
126
|
+
finishChain = resolve;
|
|
127
|
+
});
|
|
128
|
+
callMethod.mockImplementationOnce(async () => {
|
|
129
|
+
calls.push('chain-pending');
|
|
130
|
+
await chainPending;
|
|
131
|
+
if (outcome === 'failure') throw new Error('chain failed');
|
|
132
|
+
if (outcome === 'cancel') method.abortController?.abort();
|
|
133
|
+
return [{ success: true, payload: {} }] as any;
|
|
134
|
+
});
|
|
135
|
+
await expect(method.run()).resolves.toEqual([]);
|
|
136
|
+
expect(calls).toEqual(['animation:1', 'GetPublicKey', 'chain-pending']);
|
|
137
|
+
finishChain();
|
|
138
|
+
await method.device.pendingCallbackPromise?.promise;
|
|
139
|
+
expect(calls.filter(call => call === 'animation:2')).toHaveLength(
|
|
140
|
+
outcome === 'success' ? 1 : 0
|
|
141
|
+
);
|
|
142
|
+
expect(method.postMessage).toHaveBeenCalledWith(
|
|
143
|
+
expect.objectContaining({
|
|
144
|
+
payload: expect.objectContaining({ callbackId: 'finish' }),
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
test('does not send further commands when the fingerprint response is invalid', async () => {
|
|
151
|
+
const { method, typedCall, callMethod } = setup();
|
|
152
|
+
typedCall.mockResolvedValueOnce({ message: { root_fingerprint: 0 } });
|
|
153
|
+
typedCall.mockResolvedValueOnce({ message: {} } as any);
|
|
154
|
+
await expect(method.run()).rejects.toThrow();
|
|
155
|
+
expect(callMethod).not.toHaveBeenCalled();
|
|
156
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test.each(['disposed', 'replaced'])(
|
|
160
|
+
'does not send cleanup through a %s connection',
|
|
161
|
+
async state => {
|
|
162
|
+
const { method, calls, callMethod } = setup();
|
|
163
|
+
callMethod.mockImplementation(() => {
|
|
164
|
+
if (state === 'disposed') method.device.commands.disposed = true;
|
|
165
|
+
else method.device.commands = { typedCall: jest.fn() } as any;
|
|
166
|
+
return Promise.resolve([{ success: true, payload: {} }] as any);
|
|
167
|
+
});
|
|
168
|
+
await method.run();
|
|
169
|
+
expect(calls).toEqual(['animation:1', 'GetPublicKey']);
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
21
174
|
describe('AllNetworkGetAddressBase tracing', () => {
|
|
22
175
|
test('resumes a Protocol V2 hidden wallet before running a nested chain method', async () => {
|
|
23
176
|
const calls: string[] = [];
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { DeviceSessionErrorCode } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
4
|
+
import { DataManager } from '../src/data-manager';
|
|
5
|
+
import TransportManager from '../src/data-manager/TransportManager';
|
|
6
|
+
import { Device } from '../src/device/Device';
|
|
3
7
|
import { DeviceCommands } from '../src/device/DeviceCommands';
|
|
4
8
|
import { DEVICE } from '../src/events';
|
|
5
9
|
import { LoggerNames, getLogger } from '../src/utils';
|
|
@@ -807,6 +811,255 @@ describe('DeviceCommands Protocol V2 interactive response compatibility', () =>
|
|
|
807
811
|
});
|
|
808
812
|
});
|
|
809
813
|
|
|
814
|
+
describe('DeviceCommands Protocol V2 session busy retry', () => {
|
|
815
|
+
const busy = {
|
|
816
|
+
type: 'Failure',
|
|
817
|
+
message: {
|
|
818
|
+
code: 'Failure_ProcessError',
|
|
819
|
+
subcode: DeviceSessionErrorCode.DeviceSessionError_Busy,
|
|
820
|
+
message: 'Another flow in progress',
|
|
821
|
+
},
|
|
822
|
+
} as const;
|
|
823
|
+
const success = { type: 'Success', message: {} } as const;
|
|
824
|
+
const flushCalls = () =>
|
|
825
|
+
new Promise<void>(resolve => {
|
|
826
|
+
setImmediate(resolve);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
beforeEach(() => {
|
|
830
|
+
jest.useFakeTimers({ doNotFake: ['performance', 'setImmediate'] });
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
afterEach(() => {
|
|
834
|
+
jest.useRealTimers();
|
|
835
|
+
jest.restoreAllMocks();
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
const setup = () => {
|
|
839
|
+
const commands = createCommands();
|
|
840
|
+
commands.device.wasInterruptedByUser = jest.fn(() => false);
|
|
841
|
+
commands.mainId = 'main-id';
|
|
842
|
+
const call = jest.fn();
|
|
843
|
+
commands.transport = { call } as DeviceCommands['transport'];
|
|
844
|
+
return { commands, call };
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
it.each(['DeviceSessionGet', 'DeviceSessionAskPin', 'DeviceSessionAskPassphrase'] as const)(
|
|
848
|
+
'retries %s busy responses at 100 ms intervals without matching their message',
|
|
849
|
+
async request => {
|
|
850
|
+
const { commands, call } = setup();
|
|
851
|
+
const response =
|
|
852
|
+
request === 'DeviceSessionGet' ? { type: 'DeviceSession' as const, message: {} } : success;
|
|
853
|
+
call
|
|
854
|
+
.mockResolvedValueOnce(busy)
|
|
855
|
+
.mockResolvedValueOnce({
|
|
856
|
+
type: 'Failure',
|
|
857
|
+
message: { ...busy.message, message: 'Passphrase entry busy' },
|
|
858
|
+
})
|
|
859
|
+
.mockResolvedValueOnce({
|
|
860
|
+
type: 'Failure',
|
|
861
|
+
message: { ...busy.message, message: 'Passphrase confirm busy' },
|
|
862
|
+
})
|
|
863
|
+
.mockResolvedValue(response);
|
|
864
|
+
const payload = request === 'DeviceSessionAskPassphrase' ? { on_device: true } : {};
|
|
865
|
+
const result = commands.typedCall(request, response.type, payload).catch(error => error);
|
|
866
|
+
await flushCalls();
|
|
867
|
+
|
|
868
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
869
|
+
jest.advanceTimersByTime(99);
|
|
870
|
+
await flushCalls();
|
|
871
|
+
expect(call).toHaveBeenCalledTimes(attempt);
|
|
872
|
+
jest.advanceTimersByTime(1);
|
|
873
|
+
await flushCalls();
|
|
874
|
+
expect(call).toHaveBeenCalledTimes(attempt + 1);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
await expect(result).resolves.toEqual(response);
|
|
878
|
+
expect(call.mock.calls).toEqual(
|
|
879
|
+
Array.from({ length: 4 }, () => [
|
|
880
|
+
commands.mainId,
|
|
881
|
+
request,
|
|
882
|
+
payload,
|
|
883
|
+
{ expectedTypes: [response.type] },
|
|
884
|
+
])
|
|
885
|
+
);
|
|
886
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
887
|
+
}
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
it('preserves the final busy error after three retries', async () => {
|
|
891
|
+
const { commands, call } = setup();
|
|
892
|
+
call.mockResolvedValue(busy);
|
|
893
|
+
const result = commands.typedCall('DeviceSessionGet', 'DeviceSession').catch(error => error);
|
|
894
|
+
await flushCalls();
|
|
895
|
+
for (let retry = 0; retry < 3; retry += 1) {
|
|
896
|
+
jest.advanceTimersByTime(100);
|
|
897
|
+
await flushCalls();
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
expect(call).toHaveBeenCalledTimes(4);
|
|
901
|
+
await expect(result).resolves.toMatchObject({
|
|
902
|
+
errorCode: HardwareErrorCode.DeviceBusy,
|
|
903
|
+
params: {
|
|
904
|
+
failureCode: 'Failure_ProcessError',
|
|
905
|
+
subcode: DeviceSessionErrorCode.DeviceSessionError_Busy,
|
|
906
|
+
firmwareMessage: 'Another flow in progress',
|
|
907
|
+
},
|
|
908
|
+
});
|
|
909
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it('does not delay a successful session request', async () => {
|
|
913
|
+
const { commands, call } = setup();
|
|
914
|
+
call.mockResolvedValue(success);
|
|
915
|
+
await expect(commands.typedCall('DeviceSessionAskPin', 'Success')).resolves.toEqual(success);
|
|
916
|
+
expect(call).toHaveBeenCalledTimes(1);
|
|
917
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it.each([
|
|
921
|
+
['DeviceSessionAskPin', 'Failure_ProcessError', 0],
|
|
922
|
+
[
|
|
923
|
+
'DeviceSessionGet',
|
|
924
|
+
'Failure_ProcessError',
|
|
925
|
+
DeviceSessionErrorCode.DeviceSessionError_InvalidSession,
|
|
926
|
+
],
|
|
927
|
+
[
|
|
928
|
+
'DeviceSessionAskPassphrase',
|
|
929
|
+
'Failure_ProcessError',
|
|
930
|
+
DeviceSessionErrorCode.DeviceSessionError_UserCancelled,
|
|
931
|
+
],
|
|
932
|
+
['DeviceSessionGet', 'Failure_DataError', DeviceSessionErrorCode.DeviceSessionError_Busy],
|
|
933
|
+
['DeviceSettingsSet', 'Failure_ProcessError', 5],
|
|
934
|
+
['EthereumSignTx', 'Failure_ProcessError', 5],
|
|
935
|
+
] as const)('does not retry %s with %s subcode %s', async (request, code, subcode) => {
|
|
936
|
+
const { commands, call } = setup();
|
|
937
|
+
call.mockResolvedValue({ type: 'Failure', message: { code, subcode, message: 'Busy' } });
|
|
938
|
+
await expect(commands.typedCall(request, 'Success')).rejects.toBeInstanceOf(Error);
|
|
939
|
+
expect(call).toHaveBeenCalledTimes(1);
|
|
940
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
it('does not retry Protocol V1 busy responses', async () => {
|
|
944
|
+
const { commands, call } = setup();
|
|
945
|
+
commands.device.isProtocolV2 = () => false;
|
|
946
|
+
call.mockResolvedValue(busy);
|
|
947
|
+
await expect(commands.typedCall('DeviceSessionGet', 'DeviceSession')).rejects.toMatchObject({
|
|
948
|
+
errorCode: HardwareErrorCode.DeviceBusy,
|
|
949
|
+
});
|
|
950
|
+
expect(call).toHaveBeenCalledTimes(1);
|
|
951
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it('stops retrying when the next attempt fails at the transport layer', async () => {
|
|
955
|
+
const { commands, call } = setup();
|
|
956
|
+
const error = ERRORS.TypedError(HardwareErrorCode.BridgeDeviceDisconnected);
|
|
957
|
+
call.mockResolvedValueOnce(busy).mockRejectedValue(error);
|
|
958
|
+
const result = commands.typedCall('DeviceSessionGet', 'DeviceSession').catch(err => err);
|
|
959
|
+
await flushCalls();
|
|
960
|
+
jest.advanceTimersByTime(100);
|
|
961
|
+
await flushCalls();
|
|
962
|
+
await expect(result).resolves.toBe(error);
|
|
963
|
+
expect(call).toHaveBeenCalledTimes(2);
|
|
964
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
it('stops a superseded retry when a React Native run reuses the commands', async () => {
|
|
968
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
969
|
+
const call = jest.fn().mockResolvedValueOnce(busy).mockResolvedValueOnce(busy);
|
|
970
|
+
call.mockResolvedValue(success);
|
|
971
|
+
const cancel = jest.fn();
|
|
972
|
+
jest.spyOn(TransportManager, 'getTransport').mockReturnValue({
|
|
973
|
+
call,
|
|
974
|
+
cancel,
|
|
975
|
+
} as DeviceCommands['transport']);
|
|
976
|
+
const device = Device.fromDescriptor({
|
|
977
|
+
id: 'ble-device',
|
|
978
|
+
path: 'ble-device',
|
|
979
|
+
name: 'OneKey Test',
|
|
980
|
+
debug: false,
|
|
981
|
+
commType: 'ble',
|
|
982
|
+
protocolType: 'V2',
|
|
983
|
+
});
|
|
984
|
+
const commands = new DeviceCommands(device, 'ble-device');
|
|
985
|
+
device.commands = commands;
|
|
986
|
+
const release = jest.spyOn(device, 'release');
|
|
987
|
+
const continued = jest.fn();
|
|
988
|
+
let staleCallResult: Promise<unknown> | undefined;
|
|
989
|
+
const staleRun = device
|
|
990
|
+
.run(
|
|
991
|
+
async () => {
|
|
992
|
+
const operation = commands.typedCall('DeviceSessionAskPassphrase', 'Success', {
|
|
993
|
+
on_device: true,
|
|
994
|
+
});
|
|
995
|
+
staleCallResult = operation.catch(error => error);
|
|
996
|
+
await operation;
|
|
997
|
+
continued();
|
|
998
|
+
},
|
|
999
|
+
{ keepSession: true }
|
|
1000
|
+
)
|
|
1001
|
+
.catch(error => error);
|
|
1002
|
+
await flushCalls();
|
|
1003
|
+
|
|
1004
|
+
const nextRun = device.run(
|
|
1005
|
+
async () => {
|
|
1006
|
+
await commands.typedCall('DeviceSessionAskPin', 'Success');
|
|
1007
|
+
},
|
|
1008
|
+
{ keepSession: true }
|
|
1009
|
+
);
|
|
1010
|
+
await flushCalls();
|
|
1011
|
+
expect(device.commands).toBe(commands);
|
|
1012
|
+
expect(commands.disposed).toBe(false);
|
|
1013
|
+
expect(cancel).toHaveBeenCalledTimes(1);
|
|
1014
|
+
expect(call).toHaveBeenCalledTimes(2);
|
|
1015
|
+
|
|
1016
|
+
jest.advanceTimersByTime(100);
|
|
1017
|
+
await flushCalls();
|
|
1018
|
+
|
|
1019
|
+
await expect(staleRun).resolves.toMatchObject({
|
|
1020
|
+
errorCode: HardwareErrorCode.DeviceInterruptedFromOutside,
|
|
1021
|
+
});
|
|
1022
|
+
await expect(staleCallResult).resolves.toMatchObject({
|
|
1023
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
1024
|
+
});
|
|
1025
|
+
await expect(nextRun).resolves.toBeUndefined();
|
|
1026
|
+
expect(call.mock.calls.map(([, request]) => request)).toEqual([
|
|
1027
|
+
'DeviceSessionAskPassphrase',
|
|
1028
|
+
'DeviceSessionAskPin',
|
|
1029
|
+
'DeviceSessionAskPin',
|
|
1030
|
+
]);
|
|
1031
|
+
expect(continued).not.toHaveBeenCalled();
|
|
1032
|
+
expect(release).not.toHaveBeenCalled();
|
|
1033
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
it.each(['cancelled', 'disposed'] as const)(
|
|
1037
|
+
'does not resend when %s during backoff',
|
|
1038
|
+
async state => {
|
|
1039
|
+
const { commands, call } = setup();
|
|
1040
|
+
call.mockResolvedValueOnce(busy).mockResolvedValue(success);
|
|
1041
|
+
const result = commands.typedCall('DeviceSessionAskPin', 'Success').catch(error => error);
|
|
1042
|
+
await flushCalls();
|
|
1043
|
+
if (state === 'cancelled') {
|
|
1044
|
+
commands.device.wasInterruptedByUser = () => true;
|
|
1045
|
+
} else {
|
|
1046
|
+
commands.disposed = true;
|
|
1047
|
+
}
|
|
1048
|
+
jest.advanceTimersByTime(100);
|
|
1049
|
+
await flushCalls();
|
|
1050
|
+
|
|
1051
|
+
await expect(result).resolves.toMatchObject({
|
|
1052
|
+
errorCode:
|
|
1053
|
+
state === 'cancelled'
|
|
1054
|
+
? HardwareErrorCode.DeviceInterruptedFromUser
|
|
1055
|
+
: HardwareErrorCode.RuntimeError,
|
|
1056
|
+
});
|
|
1057
|
+
expect(call).toHaveBeenCalledTimes(1);
|
|
1058
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
1059
|
+
}
|
|
1060
|
+
);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
810
1063
|
describe('DeviceCommands cancellation', () => {
|
|
811
1064
|
it('stops waiting after the bounded cancellation timeout', async () => {
|
|
812
1065
|
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
@@ -30,6 +30,8 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<{
|
|
|
30
30
|
}[]> {
|
|
31
31
|
getSupportedProtocols(): readonly ["V1", "V2"];
|
|
32
32
|
abortController: AbortController | null;
|
|
33
|
+
protected loadingCleanupInBackground: boolean;
|
|
34
|
+
private loadingCommands?;
|
|
33
35
|
init(): void;
|
|
34
36
|
generateMethodName({ network, payload, originalIndex, }: {
|
|
35
37
|
network: INetwork;
|
|
@@ -42,6 +44,7 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<{
|
|
|
42
44
|
})[];
|
|
43
45
|
}, rootFingerprint: number): Promise<AllNetworkAddress[]>;
|
|
44
46
|
abstract getAllNetworkAddress(rootFingerprint: number): Promise<AllNetworkAddress[]>;
|
|
47
|
+
protected stopAllNetworkLoading(canSend?: boolean): Promise<void>;
|
|
45
48
|
run(): Promise<AllNetworkAddress[]>;
|
|
46
49
|
}
|
|
47
50
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AllNetworkGetAddressBase.d.ts","sourceRoot":"","sources":["../../../src/api/allnetwork/AllNetworkGetAddressBase.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AllNetworkGetAddressBase.d.ts","sourceRoot":"","sources":["../../../src/api/allnetwork/AllNetworkGetAddressBase.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAiB3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,QAAQ,EACT,MAAM,sCAAsC,CAAC;AAI9C,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,uBAAuB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC;IAClG,kBAAkB,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC;AAEzF,MAAM,MAAM,gBAAgB,GAAG;KAC5B,CAAC,IAAI,YAAY,GAAG,aAAa;CACnC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAOrD,CAAC;AAiMF,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,oBAAoB,EAAE,uBAAuB,CAAC;IAC9C,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,wBAAyB,SAAQ,UAAU,CACvE;IACE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,EAAE,CACJ;IACC,qBAAqB;IAIrB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAQ;IAE/C,SAAS,CAAC,0BAA0B,UAAS;IAE7C,OAAO,CAAC,eAAe,CAAC,CAAiB;IAEzC,IAAI;IAkBJ,kBAAkB,CAAC,EACjB,OAAO,EACP,OAAO,EACP,aAAa,GACd,EAAE;QACD,OAAO,EAAE,QAAQ,CAAC;QAClB,OAAO,EAAE,uBAAuB,CAAC;QACjC,aAAa,EAAE,MAAM,CAAC;KACvB,GAAG,YAAY;IAqBV,UAAU,CACd,UAAU,EAAE,MAAM,OAAO,EACzB,MAAM,EAAE,GAAG,GAAG;QACZ,MAAM,EAAE,CAAC,GAAG,GAAG;YAAE,oBAAoB,EAAE,oBAAoB,CAAA;SAAE,CAAC,EAAE,CAAC;KAClE,EACD,eAAe,EAAE,MAAM;IAyIzB,QAAQ,CAAC,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;cAEpE,qBAAqB,CAAC,OAAO,UAAO;IAc9C,GAAG;CA6CV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AllNetworkGetAddressByLoop.d.ts","sourceRoot":"","sources":["../../../src/api/allnetwork/AllNetworkGetAddressByLoop.ts"],"names":[],"mappings":"AASA,OAAO,wBAAwB,MAAM,4BAA4B,CAAC;AAQlE,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,wBAAwB;IACxE,oBAAoB,CAAC,eAAe,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"AllNetworkGetAddressByLoop.d.ts","sourceRoot":"","sources":["../../../src/api/allnetwork/AllNetworkGetAddressByLoop.ts"],"names":[],"mappings":"AASA,OAAO,wBAAwB,MAAM,4BAA4B,CAAC;AAQlE,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,wBAAwB;IACxE,oBAAoB,CAAC,eAAe,EAAE,MAAM;YA8BpC,4BAA4B;IAmF1C,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,gBAAgB;CAazB"}
|
|
@@ -62,6 +62,7 @@ export declare class DeviceCommands {
|
|
|
62
62
|
transport: Transport;
|
|
63
63
|
mainId: string;
|
|
64
64
|
disposed: boolean;
|
|
65
|
+
private disposalToken?;
|
|
65
66
|
callPromise?: Promise<DefaultMessageResponse>;
|
|
66
67
|
constructor(device: Device, mainId: string);
|
|
67
68
|
dispose(_cancelRequest: boolean): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceCommands.d.ts","sourceRoot":"","sources":["../../src/device/DeviceCommands.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,oBAAoB,EAG1B,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAU,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAQlE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,KAAK,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;AACxC,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,WAAW,EAAE,MAAM,CAAC,CAAC;AACrD,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,UAAU,IAAI;IACvD,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AACF,KAAK,oBAAoB,GAAG;KACzB,CAAC,IAAI,UAAU,GAAG,oBAAoB,CAAC,CAAC,CAAC;CAC3C,CAAC;AACF,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;AA8DtE,eAAO,MAAM,oBAAoB,WAAY,MAAM;;;;;;;;;;;;;;;;;;EA0ClD,CAAC;AAEF,eAAO,MAAM,0BAA0B,WAAY,MAAM;;;;;;;;;;;;;;;;;;EAgCxD,CAAC;AASF,qBAAa,cAAc;IACzB,UAAU,EAAE,MAAM,CAAC;IAEnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,MAAM,EAAE,MAAM,CAAC;IAEf,SAAS,EAAE,SAAS,CAAC;IAErB,MAAM,EAAE,MAAM,CAAC;IAEf,QAAQ,EAAE,OAAO,CAAC;IAElB,WAAW,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;gBAElC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAUpC,OAAO,CAAC,cAAc,EAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"DeviceCommands.d.ts","sourceRoot":"","sources":["../../src/device/DeviceCommands.ts"],"names":[],"mappings":"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"}
|