@trezor/connect 9.6.0 → 9.6.1-beta.1
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/CHANGELOG.md +36 -7
- package/README.md +1 -1
- package/lib/api/blockchainEstimateFee.js +1 -0
- package/lib/api/discoverAccounts.js +10 -2
- package/lib/api/ethereum/api/ethereumSignMessage.js +1 -1
- package/lib/api/ethereum/api/ethereumSignTypedData.d.ts +1 -1
- package/lib/api/firmware/getBinary.js +2 -2
- package/lib/api/getAccountInfo.d.ts +1 -1
- package/lib/api/getCoinInfo.d.ts +3 -3
- package/lib/api/getNonce.d.ts +9 -0
- package/lib/api/getNonce.js +15 -0
- package/lib/api/index.d.ts +1 -0
- package/lib/api/index.js +4 -2
- package/lib/api/nem/nemSignTx.d.ts +1 -1
- package/lib/api/resetDevice.d.ts +2 -3
- package/lib/api/resetDevice.js +47 -49
- package/lib/api/signMessage.js +1 -1
- package/lib/backend/Blockchain.d.ts +1 -0
- package/lib/backend/fees/BitcoinFeeLevels.d.ts +0 -1
- package/lib/backend/fees/EthereumFeeLevels.d.ts +0 -1
- package/lib/backend/fees/EthereumFeeLevels.js +8 -4
- package/lib/backend/fees/MiscFeeLevels.d.ts +0 -1
- package/lib/constants/errors.d.ts +1 -0
- package/lib/constants/errors.js +1 -0
- package/lib/core/AbstractMethod.d.ts +2 -2
- package/lib/core/AbstractMethod.js +0 -1
- package/lib/core/index.js +43 -53
- package/lib/core/onCallFirmwareUpdate.js +2 -2
- package/lib/data/coinInfo.d.ts +11 -11
- package/lib/data/coinInfo.js +1 -1
- package/lib/data/connectSettings.js +0 -3
- package/lib/data/defaultFeeLevels.d.ts +2 -1
- package/lib/data/defaultFeeLevels.js +18 -10
- package/lib/data/deviceAuthenticityConfig.js +4 -1
- package/lib/data/firmwareInfo.d.ts +1 -0
- package/lib/data/firmwareInfo.js +14 -1
- package/lib/data/version.d.ts +1 -1
- package/lib/data/version.js +1 -1
- package/lib/device/Device.d.ts +19 -20
- package/lib/device/Device.js +66 -112
- package/lib/device/DeviceCurrentSession.d.ts +441 -4027
- package/lib/device/DeviceCurrentSession.js +75 -44
- package/lib/device/DeviceList.d.ts +3 -8
- package/lib/device/DeviceList.js +21 -77
- package/lib/device/TransportManager.d.ts +2 -6
- package/lib/device/TransportManager.js +11 -7
- package/lib/device/checkFirmwareRevision.js +2 -2
- package/lib/device/workflow/index.d.ts +2 -0
- package/lib/device/workflow/index.js +6 -0
- package/lib/device/workflow/validateState.d.ts +3 -0
- package/lib/device/workflow/validateState.js +90 -0
- package/lib/events/device.d.ts +19 -1
- package/lib/events/device.js +2 -0
- package/lib/events/index.d.ts +3 -0
- package/lib/events/ui-request.d.ts +17 -3
- package/lib/events/ui-request.js +2 -0
- package/lib/events/ui-response.d.ts +11 -1
- package/lib/events/ui-response.js +1 -0
- package/lib/factory.js +1 -0
- package/lib/tsconfig.lib.tsbuildinfo +1 -1
- package/lib/types/api/applySettings.d.ts +1 -0
- package/lib/types/api/discoverAccounts.d.ts +17 -4
- package/lib/types/api/discoverAccounts.js +2 -0
- package/lib/types/api/getNonce.d.ts +4 -0
- package/lib/types/api/getNonce.js +3 -0
- package/lib/types/api/index.d.ts +2 -0
- package/lib/types/coinInfo.d.ts +6 -6
- package/lib/types/coinInfo.js +2 -0
- package/lib/types/fees.d.ts +1 -1
- package/lib/types/fees.js +1 -1
- package/lib/types/settings.d.ts +0 -1
- package/lib/types/workflow.d.ts +8 -0
- package/lib/types/workflow.js +3 -0
- package/lib/utils/formatUtils.d.ts +1 -0
- package/lib/utils/formatUtils.js +9 -1
- package/lib/utils/uiPromiseManager.d.ts +2 -0
- package/package.json +16 -16
- package/lib/data/downloadReleasesMetadata.d.ts +0 -7
- package/lib/data/downloadReleasesMetadata.js +0 -18
- package/lib/data/models.d.ts +0 -8
- package/lib/data/models.js +0 -49
|
@@ -3,8 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DeviceCurrentSession = void 0;
|
|
4
4
|
const protobuf_1 = require("@trezor/protobuf");
|
|
5
5
|
const schema_utils_1 = require("@trezor/schema-utils");
|
|
6
|
+
const transport_1 = require("@trezor/transport");
|
|
7
|
+
const errors_groups_1 = require("@trezor/transport/lib/errors-groups");
|
|
6
8
|
const utils_1 = require("@trezor/utils");
|
|
7
9
|
const constants_1 = require("../constants");
|
|
10
|
+
const DataManager_1 = require("../data/DataManager");
|
|
8
11
|
const events_1 = require("../events");
|
|
9
12
|
const debug_1 = require("../utils/debug");
|
|
10
13
|
const blacklist = {
|
|
@@ -15,31 +18,52 @@ const blacklist = {
|
|
|
15
18
|
DecryptedMessage: ['message', 'address'],
|
|
16
19
|
Features: true,
|
|
17
20
|
};
|
|
21
|
+
const allowedCallsBeforeInitialize = [
|
|
22
|
+
'Cancel',
|
|
23
|
+
'Initialize',
|
|
24
|
+
'GetFeatures',
|
|
25
|
+
'GetFirmwareHash',
|
|
26
|
+
'ChangeLanguage',
|
|
27
|
+
'DataChunkAck',
|
|
28
|
+
'FirmwareErase',
|
|
29
|
+
'FirmwareUpload',
|
|
30
|
+
];
|
|
18
31
|
const filterForLog = (type, msg) => blacklist[type] === true
|
|
19
32
|
? '(redacted...)'
|
|
20
33
|
: (blacklist[type] ?? []).reduce((prev, cur) => ({ ...prev, [cur]: '(redacted...)' }), msg);
|
|
21
34
|
const logger = (0, debug_1.initLog)('DeviceCommands');
|
|
35
|
+
const isExpectedResponse = (response, expected) => (Array.isArray(expected) ? expected : expected.split('|')).includes(response.type);
|
|
22
36
|
const success = (payload) => ({ success: true, payload });
|
|
23
37
|
const error = (error) => ({ success: false, error });
|
|
24
|
-
const fail = (msg
|
|
38
|
+
const fail = (msg) => error(new Error(msg, (0, errors_groups_1.isErrorWithoutDeviceInteraction)(msg) ? { cause: 'transport-error' } : undefined));
|
|
39
|
+
const getFeaturesTimeout = () => DataManager_1.DataManager.getSettings('env') === 'react-native' ? 20_000 : 3_000;
|
|
25
40
|
class DeviceCurrentSession {
|
|
26
41
|
device;
|
|
27
42
|
transport;
|
|
28
|
-
protocol;
|
|
29
43
|
session;
|
|
30
44
|
disposed;
|
|
31
45
|
callPromise;
|
|
32
46
|
abortController;
|
|
33
|
-
constructor(device, transport,
|
|
47
|
+
constructor(device, transport, session) {
|
|
34
48
|
this.device = device;
|
|
35
49
|
this.transport = transport;
|
|
36
|
-
this.protocol = protocol;
|
|
37
50
|
this.session = session;
|
|
51
|
+
transport.deviceEvents.once(device.transportPath, e => {
|
|
52
|
+
if (!this.disposed) {
|
|
53
|
+
this.disposed = constants_1.ERRORS.TypedError(e.type === transport_1.TRANSPORT.DEVICE_DISCONNECTED
|
|
54
|
+
? 'Device_Disconnected'
|
|
55
|
+
: 'Device_UsedElsewhere');
|
|
56
|
+
this.abortController?.abort(this.disposed);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
38
59
|
}
|
|
39
60
|
isDisposed() {
|
|
40
61
|
return !!this.disposed;
|
|
41
62
|
}
|
|
42
|
-
async typedCall(type,
|
|
63
|
+
async typedCall(type, expectedType, msg = {}) {
|
|
64
|
+
if (!allowedCallsBeforeInitialize.includes(type) && !this.device?.features?.session_id) {
|
|
65
|
+
console.error('Runtime', `typedCall: Device not initialized when calling ${type}. call Initialize first`);
|
|
66
|
+
}
|
|
43
67
|
(0, schema_utils_1.Assert)(protobuf_1.MessagesSchema.MessageType.properties[type], msg);
|
|
44
68
|
this.abortController = new AbortController();
|
|
45
69
|
const { signal } = this.abortController;
|
|
@@ -52,15 +76,19 @@ class DeviceCurrentSession {
|
|
|
52
76
|
if (!response.success)
|
|
53
77
|
throw response.error;
|
|
54
78
|
const { payload } = response;
|
|
55
|
-
|
|
79
|
+
const receivedType = payload.type;
|
|
80
|
+
if (isExpectedResponse(payload, expectedType)) {
|
|
81
|
+
return payload;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
56
84
|
await (0, utils_1.scheduleAction)(abort => this.transport.receive({
|
|
57
85
|
session: this.session,
|
|
58
|
-
protocol: this.protocol,
|
|
86
|
+
protocol: this.device.protocol,
|
|
87
|
+
thpState: this.device.getThpState(),
|
|
59
88
|
signal: abort,
|
|
60
89
|
}), { timeout: 500 }).catch(() => { });
|
|
61
|
-
throw constants_1.ERRORS.TypedError('Runtime', `assertType: Response of unexpected type: ${
|
|
90
|
+
throw constants_1.ERRORS.TypedError('Runtime', `assertType: Response of unexpected type: ${receivedType}. Should be ${expectedType}`);
|
|
62
91
|
}
|
|
63
|
-
return payload;
|
|
64
92
|
}
|
|
65
93
|
needCancelWorkaround() {
|
|
66
94
|
return (this.transport.name === 'BridgeTransport' &&
|
|
@@ -68,32 +96,36 @@ class DeviceCurrentSession {
|
|
|
68
96
|
}
|
|
69
97
|
async callLoop(type, msg, abortPromise) {
|
|
70
98
|
let [name, data] = [type, msg];
|
|
71
|
-
const { protocol, session } = this;
|
|
72
99
|
let pinUnlocked = false;
|
|
73
100
|
while (true) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
abortPromise.then(() => true),
|
|
101
|
+
const timeout = name === 'GetFeatures' ? getFeaturesTimeout() : undefined;
|
|
102
|
+
const callPromise = this.call(name, data, timeout);
|
|
103
|
+
const [abortedDuringCall, response] = await Promise.race([
|
|
104
|
+
callPromise.then(res => [false, res]),
|
|
105
|
+
abortPromise.then(res => [true, res]),
|
|
80
106
|
]);
|
|
81
107
|
if (name === 'ButtonAck' && abortedDuringCall && !this.disposed) {
|
|
82
108
|
if (this.needCancelWorkaround()) {
|
|
83
109
|
try {
|
|
84
110
|
await (0, utils_1.resolveAfter)(1);
|
|
85
111
|
await this.device.acquire();
|
|
86
|
-
await this.device.getCurrentSession().cancelCall(
|
|
112
|
+
await this.device.getCurrentSession().cancelCall();
|
|
87
113
|
await this.device.release();
|
|
88
114
|
}
|
|
89
115
|
catch {
|
|
90
116
|
}
|
|
91
117
|
}
|
|
92
118
|
else {
|
|
93
|
-
await this.
|
|
119
|
+
await this.transport.send({
|
|
120
|
+
name: 'Cancel',
|
|
121
|
+
data: {},
|
|
122
|
+
session: this.session,
|
|
123
|
+
protocol: this.device.protocol,
|
|
124
|
+
thpState: this.device.getThpState(),
|
|
125
|
+
});
|
|
94
126
|
}
|
|
95
127
|
}
|
|
96
|
-
|
|
128
|
+
await callPromise;
|
|
97
129
|
if (this.disposed)
|
|
98
130
|
return error(this.disposed);
|
|
99
131
|
if (!response.success)
|
|
@@ -102,6 +134,10 @@ class DeviceCurrentSession {
|
|
|
102
134
|
switch (res.type) {
|
|
103
135
|
case 'Failure': {
|
|
104
136
|
const { code, message } = res.message;
|
|
137
|
+
if (name === 'GetFeatures' && code === 'Failure_UnexpectedMessage') {
|
|
138
|
+
[name, data] = ['Initialize', {}];
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
105
141
|
const err = message ||
|
|
106
142
|
(code === 'Failure_FirmwareError' && 'Firmware installation failed') ||
|
|
107
143
|
(code === 'Failure_ActionCancelled' && 'Action cancelled by user') ||
|
|
@@ -127,7 +163,7 @@ class DeviceCurrentSession {
|
|
|
127
163
|
abortPromise,
|
|
128
164
|
]);
|
|
129
165
|
if (!promptRes.success) {
|
|
130
|
-
const cancelRes = await this.
|
|
166
|
+
const cancelRes = await this.call('Cancel', {});
|
|
131
167
|
return cancelRes.success ? promptRes : cancelRes;
|
|
132
168
|
}
|
|
133
169
|
pinUnlocked = true;
|
|
@@ -140,7 +176,7 @@ class DeviceCurrentSession {
|
|
|
140
176
|
abortPromise,
|
|
141
177
|
]);
|
|
142
178
|
if (!promptRes.success) {
|
|
143
|
-
const cancelRes = await this.
|
|
179
|
+
const cancelRes = await this.call('Cancel', {});
|
|
144
180
|
return cancelRes.success ? promptRes : cancelRes;
|
|
145
181
|
}
|
|
146
182
|
const payload = promptRes.payload.passphraseOnDevice
|
|
@@ -155,7 +191,7 @@ class DeviceCurrentSession {
|
|
|
155
191
|
abortPromise,
|
|
156
192
|
]);
|
|
157
193
|
if (!promptRes.success) {
|
|
158
|
-
const cancelRes = await this.
|
|
194
|
+
const cancelRes = await this.call('Cancel', {});
|
|
159
195
|
return cancelRes.success ? promptRes : cancelRes;
|
|
160
196
|
}
|
|
161
197
|
[name, data] = ['WordAck', { word: promptRes.payload }];
|
|
@@ -170,9 +206,18 @@ class DeviceCurrentSession {
|
|
|
170
206
|
}
|
|
171
207
|
}
|
|
172
208
|
}
|
|
173
|
-
async call(
|
|
174
|
-
|
|
175
|
-
|
|
209
|
+
async call(name, data, timeout) {
|
|
210
|
+
if (this.disposed)
|
|
211
|
+
return Promise.resolve(error(this.disposed));
|
|
212
|
+
logger.debug('Sending', name, filterForLog(name, data));
|
|
213
|
+
const result = await this.transport.call({
|
|
214
|
+
name,
|
|
215
|
+
data,
|
|
216
|
+
session: this.session,
|
|
217
|
+
protocol: this.device.protocol,
|
|
218
|
+
thpState: this.device.getThpState(),
|
|
219
|
+
timeout,
|
|
220
|
+
});
|
|
176
221
|
if (result.success) {
|
|
177
222
|
const { type, message } = result.payload;
|
|
178
223
|
logger.debug('Received', type, filterForLog(type, message));
|
|
@@ -180,29 +225,15 @@ class DeviceCurrentSession {
|
|
|
180
225
|
else {
|
|
181
226
|
logger.warn('Received transport error', result.error, result.message);
|
|
182
227
|
}
|
|
183
|
-
return result.success ? success(result.payload) : fail(result.error
|
|
228
|
+
return result.success ? success(result.payload) : fail(result.error);
|
|
184
229
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return Promise.resolve(error(this.disposed));
|
|
188
|
-
const { protocol, session } = this;
|
|
189
|
-
const cancelArgs = { session, name: 'Cancel', data: {}, protocol };
|
|
190
|
-
const response = expectResponse
|
|
191
|
-
? await this.transport.call(cancelArgs)
|
|
192
|
-
: await this.transport.send(cancelArgs);
|
|
193
|
-
return response.success ? success(response.payload) : fail(response.error);
|
|
230
|
+
cancelCall() {
|
|
231
|
+
return this.call('Cancel', {});
|
|
194
232
|
}
|
|
195
|
-
async abort(reason
|
|
233
|
+
async abort(reason) {
|
|
196
234
|
this.abortController?.abort(reason);
|
|
197
235
|
await this.callPromise;
|
|
198
|
-
|
|
199
|
-
this.disposed = reason;
|
|
200
|
-
}
|
|
201
|
-
async dispose() {
|
|
202
|
-
if (!this.disposed) {
|
|
203
|
-
this.disposed = constants_1.ERRORS.TypedError('Runtime', 'typedCall: DeviceCommands already disposed');
|
|
204
|
-
await this.abort(this.disposed);
|
|
205
|
-
}
|
|
236
|
+
this.disposed = reason;
|
|
206
237
|
}
|
|
207
238
|
}
|
|
208
239
|
exports.DeviceCurrentSession = DeviceCurrentSession;
|
|
@@ -23,14 +23,14 @@ export interface IDeviceList {
|
|
|
23
23
|
dispose: DeviceList['dispose'];
|
|
24
24
|
}
|
|
25
25
|
export declare const assertDeviceListConnected: (deviceList: IDeviceList) => asserts deviceList is DeviceList;
|
|
26
|
-
type ConstructorParams = Pick<ConnectSettings, 'priority' | 'debug' | '
|
|
26
|
+
type ConstructorParams = Pick<ConnectSettings, 'priority' | 'debug' | 'manifest'> & {
|
|
27
27
|
messages: Record<string, any>;
|
|
28
28
|
};
|
|
29
29
|
type InitParams = Pick<ConnectSettings, 'transports' | 'pendingTransportEvent' | 'transportReconnect'>;
|
|
30
30
|
export declare class DeviceList extends TypedEmitter<DeviceListEvents> implements IDeviceList {
|
|
31
31
|
private readonly transportManagers;
|
|
32
32
|
private transports;
|
|
33
|
-
private
|
|
33
|
+
private devices;
|
|
34
34
|
private deviceCounter;
|
|
35
35
|
private readonly handshakeLock;
|
|
36
36
|
private readonly authPenaltyManager;
|
|
@@ -44,14 +44,10 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
|
|
|
44
44
|
version: string;
|
|
45
45
|
outdated: boolean;
|
|
46
46
|
}[];
|
|
47
|
-
constructor({ messages, priority, debug,
|
|
47
|
+
constructor({ messages, priority, debug, manifest }: ConstructorParams);
|
|
48
48
|
private onDeviceConnected;
|
|
49
|
-
private onDeviceDisconnected;
|
|
50
|
-
private onDeviceSessionChanged;
|
|
51
|
-
private onDeviceRequestRelease;
|
|
52
49
|
private getOrCreateTransportManager;
|
|
53
50
|
init({ transports, transportReconnect, pendingTransportEvent }?: InitParams): Promise<void>;
|
|
54
|
-
private startTransport;
|
|
55
51
|
private initializeTransport;
|
|
56
52
|
private waitForDevices;
|
|
57
53
|
getDeviceCount(): number;
|
|
@@ -60,7 +56,6 @@ export declare class DeviceList extends TypedEmitter<DeviceListEvents> implement
|
|
|
60
56
|
getDeviceByPath(path: DeviceUniquePath): Device | undefined;
|
|
61
57
|
getDeviceByStaticState(state: StaticSessionId): Device | undefined;
|
|
62
58
|
dispose(): Promise<void>;
|
|
63
|
-
private stopTransport;
|
|
64
59
|
enumerate(): Promise<void>;
|
|
65
60
|
addAuthPenalty(device: Device): void;
|
|
66
61
|
removeAuthPenalty(device: Device): void;
|
package/lib/device/DeviceList.js
CHANGED
|
@@ -30,30 +30,6 @@ const createAuthPenaltyManager = (priority) => {
|
|
|
30
30
|
const clear = () => Object.keys(penalizedDevices).forEach(key => delete penalizedDevices[key]);
|
|
31
31
|
return { get, add, remove, clear };
|
|
32
32
|
};
|
|
33
|
-
const createDeviceCollection = () => {
|
|
34
|
-
let devices = [];
|
|
35
|
-
const isEqual = (a) => (b) => a.transport === b.transport && a.transportPath === b.transportPath;
|
|
36
|
-
const get = (transportPath, transport) => devices.find(isEqual({ transport, transportPath }));
|
|
37
|
-
const all = () => devices;
|
|
38
|
-
const add = (device) => {
|
|
39
|
-
const index = devices.findIndex(isEqual(device));
|
|
40
|
-
if (index >= 0)
|
|
41
|
-
devices[index] = device;
|
|
42
|
-
else
|
|
43
|
-
devices.push(device);
|
|
44
|
-
};
|
|
45
|
-
const remove = (transportPath, transport) => {
|
|
46
|
-
const index = devices.findIndex(isEqual({ transport, transportPath }));
|
|
47
|
-
const [removed] = index >= 0 ? devices.splice(index, 1) : [undefined];
|
|
48
|
-
return removed;
|
|
49
|
-
};
|
|
50
|
-
const clear = (transport) => {
|
|
51
|
-
let removed;
|
|
52
|
-
[removed, devices] = (0, utils_1.arrayPartition)(devices, d => !transport || d.transport === transport);
|
|
53
|
-
return removed;
|
|
54
|
-
};
|
|
55
|
-
return { get, all, add, remove, clear };
|
|
56
|
-
};
|
|
57
33
|
const getTransportInfo = (transport) => ({
|
|
58
34
|
apiType: transport.apiType,
|
|
59
35
|
type: transport.name,
|
|
@@ -69,7 +45,7 @@ exports.assertDeviceListConnected = assertDeviceListConnected;
|
|
|
69
45
|
class DeviceList extends utils_1.TypedEmitter {
|
|
70
46
|
transportManagers = {};
|
|
71
47
|
transports = [];
|
|
72
|
-
devices =
|
|
48
|
+
devices = [];
|
|
73
49
|
deviceCounter = Date.now();
|
|
74
50
|
handshakeLock;
|
|
75
51
|
authPenaltyManager;
|
|
@@ -92,7 +68,7 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
92
68
|
getActiveTransports() {
|
|
93
69
|
return this.getConnectedTransports().map(getTransportInfo);
|
|
94
70
|
}
|
|
95
|
-
constructor({ messages, priority, debug,
|
|
71
|
+
constructor({ messages, priority, debug, manifest }) {
|
|
96
72
|
super();
|
|
97
73
|
const transportLogger = (0, debug_1.initLog)('@trezor/transport', debug);
|
|
98
74
|
this.handshakeLock = (0, utils_1.getSynchronize)();
|
|
@@ -100,7 +76,6 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
100
76
|
this.updateTransports = (0, TransportList_1.createTransportList)({
|
|
101
77
|
messages,
|
|
102
78
|
logger: transportLogger,
|
|
103
|
-
sessionsBackgroundUrl: _sessionsBackgroundUrl,
|
|
104
79
|
id: manifest?.appName || manifest?.appUrl || 'unknown app',
|
|
105
80
|
});
|
|
106
81
|
}
|
|
@@ -110,34 +85,27 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
110
85
|
id: (0, types_1.DeviceUniquePath)(id),
|
|
111
86
|
transport,
|
|
112
87
|
descriptor,
|
|
113
|
-
listener: lifecycle =>
|
|
88
|
+
listener: lifecycle => {
|
|
89
|
+
if (lifecycle === events_1.DEVICE.DISCONNECT) {
|
|
90
|
+
this.authPenaltyManager.remove(device);
|
|
91
|
+
const index = this.devices.indexOf(device);
|
|
92
|
+
if (index >= 0)
|
|
93
|
+
this.devices.splice(index, 1);
|
|
94
|
+
}
|
|
95
|
+
this.emit(lifecycle, device);
|
|
96
|
+
},
|
|
114
97
|
});
|
|
115
|
-
this.devices.
|
|
98
|
+
this.devices.push(device);
|
|
116
99
|
const penalty = this.authPenaltyManager.get();
|
|
117
100
|
this.handshakeLock(async () => {
|
|
118
|
-
if (this.devices.
|
|
101
|
+
if (this.devices.includes(device)) {
|
|
119
102
|
await device.handshake(penalty);
|
|
120
103
|
}
|
|
121
104
|
});
|
|
122
105
|
}
|
|
123
|
-
onDeviceDisconnected(descriptor, transport) {
|
|
124
|
-
const device = this.devices.remove(descriptor.path, transport);
|
|
125
|
-
device?.disconnect();
|
|
126
|
-
}
|
|
127
|
-
onDeviceSessionChanged(descriptor, transport) {
|
|
128
|
-
const device = this.devices.get(descriptor.path, transport);
|
|
129
|
-
device?.updateDescriptor(descriptor);
|
|
130
|
-
}
|
|
131
|
-
onDeviceRequestRelease(descriptor, transport) {
|
|
132
|
-
const device = this.devices.get(descriptor.path, transport);
|
|
133
|
-
device?.usedElsewhere();
|
|
134
|
-
}
|
|
135
106
|
getOrCreateTransportManager(apiType) {
|
|
136
107
|
if (!this.transportManagers[apiType]) {
|
|
137
|
-
const manager = new TransportManager_1.TransportManager(
|
|
138
|
-
startTransport: this.startTransport.bind(this),
|
|
139
|
-
stopTransport: this.stopTransport.bind(this),
|
|
140
|
-
});
|
|
108
|
+
const manager = new TransportManager_1.TransportManager(this.initializeTransport.bind(this));
|
|
141
109
|
manager.on(transport_1.TRANSPORT.START, transport => this.emit(transport_1.TRANSPORT.START, getTransportInfo(transport)));
|
|
142
110
|
manager.on(transport_1.TRANSPORT.ERROR, error => this.emit(transport_1.TRANSPORT.ERROR, { apiType, error }));
|
|
143
111
|
this.transportManagers[apiType] = manager;
|
|
@@ -157,20 +125,8 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
157
125
|
}));
|
|
158
126
|
await Promise.all(promises);
|
|
159
127
|
}
|
|
160
|
-
async startTransport(transport, pendingTransportEvent, signal) {
|
|
161
|
-
try {
|
|
162
|
-
await this.initializeTransport(transport, pendingTransportEvent, signal);
|
|
163
|
-
}
|
|
164
|
-
catch (err) {
|
|
165
|
-
this.stopTransport(transport);
|
|
166
|
-
throw err;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
128
|
async initializeTransport(transport, pendingTransportEvent, signal) {
|
|
170
129
|
transport.on(transport_1.TRANSPORT.DEVICE_CONNECTED, d => this.onDeviceConnected(d, transport));
|
|
171
|
-
transport.on(transport_1.TRANSPORT.DEVICE_DISCONNECTED, d => this.onDeviceDisconnected(d, transport));
|
|
172
|
-
transport.on(transport_1.TRANSPORT.DEVICE_SESSION_CHANGED, d => this.onDeviceSessionChanged(d, transport));
|
|
173
|
-
transport.on(transport_1.TRANSPORT.DEVICE_REQUEST_RELEASE, d => this.onDeviceRequestRelease(d, transport));
|
|
174
130
|
const enumerateResult = await transport.enumerate({ signal });
|
|
175
131
|
if (!enumerateResult.success) {
|
|
176
132
|
throw new Error(enumerateResult.error);
|
|
@@ -211,44 +167,32 @@ class DeviceList extends utils_1.TypedEmitter {
|
|
|
211
167
|
});
|
|
212
168
|
}
|
|
213
169
|
getDeviceCount() {
|
|
214
|
-
return this.devices.
|
|
170
|
+
return this.devices.length;
|
|
215
171
|
}
|
|
216
172
|
getAllDevices() {
|
|
217
|
-
return this.devices
|
|
173
|
+
return this.devices;
|
|
218
174
|
}
|
|
219
175
|
getOnlyDevice() {
|
|
220
|
-
return this.
|
|
176
|
+
return this.devices.length === 1 ? this.devices[0] : undefined;
|
|
221
177
|
}
|
|
222
178
|
getDeviceByPath(path) {
|
|
223
|
-
return this.devices.
|
|
179
|
+
return this.devices.find(d => d.getUniquePath() === path);
|
|
224
180
|
}
|
|
225
181
|
getDeviceByStaticState(state) {
|
|
226
182
|
const deviceId = state.split('@')[1].split(':')[0];
|
|
227
|
-
return this.devices.
|
|
183
|
+
return this.devices.find(d => d.features?.device_id === deviceId);
|
|
228
184
|
}
|
|
229
185
|
async dispose() {
|
|
230
186
|
this.removeAllListeners();
|
|
231
187
|
const promises = Object.values(this.transportManagers).map(manager => manager.dispose());
|
|
232
188
|
await Promise.all(promises);
|
|
233
189
|
}
|
|
234
|
-
stopTransport(transport) {
|
|
235
|
-
const devices = this.devices.clear(transport);
|
|
236
|
-
devices.forEach(device => {
|
|
237
|
-
this.emit(events_1.DEVICE.DISCONNECT, device);
|
|
238
|
-
this.authPenaltyManager.remove(device);
|
|
239
|
-
device.dispose();
|
|
240
|
-
});
|
|
241
|
-
transport?.stop();
|
|
242
|
-
}
|
|
243
190
|
async enumerate() {
|
|
244
191
|
const promises = this.getConnectedTransports().map(async (transport) => {
|
|
245
192
|
const res = await transport.enumerate();
|
|
246
|
-
if (
|
|
247
|
-
|
|
193
|
+
if (res.success) {
|
|
194
|
+
transport.handleDescriptorsChange(res.payload);
|
|
248
195
|
}
|
|
249
|
-
res.payload.forEach(d => {
|
|
250
|
-
this.devices.get(d.path, transport)?.updateDescriptor(d);
|
|
251
|
-
});
|
|
252
196
|
});
|
|
253
197
|
await Promise.all(promises);
|
|
254
198
|
}
|
|
@@ -4,10 +4,7 @@ type TransportManagerEvents = {
|
|
|
4
4
|
[TRANSPORT.START]: Transport;
|
|
5
5
|
[TRANSPORT.ERROR]: string;
|
|
6
6
|
};
|
|
7
|
-
type
|
|
8
|
-
startTransport: (transport: Transport, pendingTransportEvent: boolean, signal: AbortSignal) => Promise<void>;
|
|
9
|
-
stopTransport: (transport: Transport) => void;
|
|
10
|
-
};
|
|
7
|
+
type StartTransport = (transport: Transport, pendingTransportEvent: boolean, signal: AbortSignal) => Promise<void>;
|
|
11
8
|
type InitParams = {
|
|
12
9
|
transports: Transport[];
|
|
13
10
|
transportReconnect?: boolean;
|
|
@@ -20,8 +17,7 @@ export declare class TransportManager extends TypedEmitter<TransportManagerEvent
|
|
|
20
17
|
private transportReconnect;
|
|
21
18
|
private upgradeTimeout?;
|
|
22
19
|
private readonly startTransport;
|
|
23
|
-
|
|
24
|
-
constructor({ startTransport, stopTransport }: TransportManagerParams);
|
|
20
|
+
constructor(startTransport: StartTransport);
|
|
25
21
|
pending(): Promise<void> | undefined;
|
|
26
22
|
get(): Transport | undefined;
|
|
27
23
|
init({ transports, transportReconnect, pendingTransportEvent }: InitParams): Promise<void>;
|
|
@@ -34,11 +34,9 @@ class TransportManager extends utils_1.TypedEmitter {
|
|
|
34
34
|
transportReconnect = false;
|
|
35
35
|
upgradeTimeout;
|
|
36
36
|
startTransport;
|
|
37
|
-
|
|
38
|
-
constructor({ startTransport, stopTransport }) {
|
|
37
|
+
constructor(startTransport) {
|
|
39
38
|
super();
|
|
40
39
|
this.startTransport = startTransport;
|
|
41
|
-
this.stopTransport = stopTransport;
|
|
42
40
|
}
|
|
43
41
|
pending() {
|
|
44
42
|
return this.lock.getPending();
|
|
@@ -58,7 +56,7 @@ class TransportManager extends utils_1.TypedEmitter {
|
|
|
58
56
|
if (activeTransport) {
|
|
59
57
|
clearTimeout(this.upgradeTimeout);
|
|
60
58
|
delete this.activeTransport;
|
|
61
|
-
|
|
59
|
+
activeTransport.stop();
|
|
62
60
|
}
|
|
63
61
|
return Promise.resolve();
|
|
64
62
|
});
|
|
@@ -104,17 +102,23 @@ class TransportManager extends utils_1.TypedEmitter {
|
|
|
104
102
|
if (activeTransport) {
|
|
105
103
|
clearTimeout(this.upgradeTimeout);
|
|
106
104
|
delete this.activeTransport;
|
|
107
|
-
|
|
105
|
+
activeTransport.stop();
|
|
108
106
|
}
|
|
109
107
|
if (transport) {
|
|
110
|
-
|
|
108
|
+
try {
|
|
109
|
+
await this.startTransport(transport, pendingTransportEvent, abortSignal);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
transport.stop();
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
111
115
|
transport.on(transport_1.TRANSPORT.ERROR, error => {
|
|
112
116
|
this.emit(transport_1.TRANSPORT.ERROR, error);
|
|
113
117
|
clearTimeout(this.upgradeTimeout);
|
|
114
118
|
this.lock
|
|
115
119
|
.override('Transport error', async (signal) => {
|
|
116
120
|
delete this.activeTransport;
|
|
117
|
-
|
|
121
|
+
transport.stop();
|
|
118
122
|
if (this.transportReconnect) {
|
|
119
123
|
await (0, utils_1.resolveAfter)(1000, signal);
|
|
120
124
|
await this.createInitPromise(pendingTransportEvent, signal);
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.checkFirmwareRevision = void 0;
|
|
4
4
|
const utils_1 = require("@trezor/utils");
|
|
5
5
|
const calculateRevisionForDevice_1 = require("./calculateRevisionForDevice");
|
|
6
|
-
const
|
|
6
|
+
const firmwareInfo_1 = require("../data/firmwareInfo");
|
|
7
7
|
const assets_browser_1 = require("../utils/assets-browser");
|
|
8
8
|
const isNotFoundError = (e) => e instanceof assets_browser_1.HttpRequestError && e.response.status === 404;
|
|
9
9
|
const isNodeJSOfflineError = (e) => ['FetchError', 'AbortError'].includes(e.name);
|
|
@@ -14,7 +14,7 @@ const isOfflineError = (e) => {
|
|
|
14
14
|
return isNodeJSOfflineError(e) || isReactNativeOfflineError(e);
|
|
15
15
|
};
|
|
16
16
|
const getOnlineReleaseMetadata = async ({ firmwareVersion, internalModel, }) => {
|
|
17
|
-
const onlineReleases = await (0,
|
|
17
|
+
const onlineReleases = await (0, firmwareInfo_1.getOnlineReleases)(internalModel);
|
|
18
18
|
return onlineReleases.find(onlineRelease => utils_1.versionUtils.isEqual(onlineRelease.version, firmwareVersion));
|
|
19
19
|
};
|
|
20
20
|
const failFirmwareRevisionCheck = (error, errorPayload) => ({
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateState = void 0;
|
|
4
|
+
var validateState_1 = require("./validateState");
|
|
5
|
+
Object.defineProperty(exports, "validateState", { enumerable: true, get: function () { return validateState_1.validateState; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateState = void 0;
|
|
4
|
+
const constants_1 = require("../../constants");
|
|
5
|
+
const DataManager_1 = require("../../data/DataManager");
|
|
6
|
+
const events_1 = require("../../events");
|
|
7
|
+
const pathUtils_1 = require("../../utils/pathUtils");
|
|
8
|
+
const getState = async ({ device, method }) => {
|
|
9
|
+
if (!device.features)
|
|
10
|
+
return;
|
|
11
|
+
if (!device.features.unlocked && method.preauthorized) {
|
|
12
|
+
if (await device.getCommands().preauthorize(false)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const expectedState = device.getState()?.staticSessionId;
|
|
17
|
+
const { message } = await device.getCurrentSession().typedCall('GetAddress', 'Address', {
|
|
18
|
+
address_n: [(0, pathUtils_1.toHardened)(44), (0, pathUtils_1.toHardened)(1), (0, pathUtils_1.toHardened)(0), 0, 0],
|
|
19
|
+
coin_name: 'Testnet',
|
|
20
|
+
script_type: 'SPENDADDRESS',
|
|
21
|
+
});
|
|
22
|
+
const uniqueState = `${message.address}@${device.features.device_id}:${device.getInstance()}`;
|
|
23
|
+
if (device.features.session_id) {
|
|
24
|
+
device.setState({ sessionId: device.features.session_id });
|
|
25
|
+
}
|
|
26
|
+
if (expectedState && expectedState !== uniqueState) {
|
|
27
|
+
return uniqueState;
|
|
28
|
+
}
|
|
29
|
+
if (!expectedState) {
|
|
30
|
+
device.setState({ staticSessionId: uniqueState });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const MAX_PIN_TRIES = 3;
|
|
34
|
+
const getInvalidDeviceState = async (context) => {
|
|
35
|
+
for (let i = 0; i < MAX_PIN_TRIES - 1; ++i) {
|
|
36
|
+
try {
|
|
37
|
+
return await getState(context);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error.message.includes('PIN invalid')) {
|
|
41
|
+
context.method.postMessage((0, events_1.createUiMessage)(events_1.UI.INVALID_PIN, { device: context.device.toMessageObject() }));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return getState(context);
|
|
49
|
+
};
|
|
50
|
+
const validateState = async (context) => {
|
|
51
|
+
const { device, method } = context;
|
|
52
|
+
if (!method.useDeviceState) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const isDeviceUnlocked = device.features.unlocked;
|
|
56
|
+
const isUsingPopup = DataManager_1.DataManager.getSettings('popup');
|
|
57
|
+
try {
|
|
58
|
+
let invalidDeviceState = await getInvalidDeviceState(context);
|
|
59
|
+
if (isUsingPopup) {
|
|
60
|
+
while (invalidDeviceState) {
|
|
61
|
+
const uiPromise = method.createUiPromise(events_1.UI.INVALID_PASSPHRASE_ACTION, device);
|
|
62
|
+
method.postMessage((0, events_1.createUiMessage)(events_1.UI.INVALID_PASSPHRASE, {
|
|
63
|
+
device: device.toMessageObject(),
|
|
64
|
+
}));
|
|
65
|
+
const uiResp = await uiPromise.promise;
|
|
66
|
+
if (uiResp.payload) {
|
|
67
|
+
device.setState({ sessionId: undefined });
|
|
68
|
+
await device.initialize(method.useCardanoDerivation);
|
|
69
|
+
invalidDeviceState = await getInvalidDeviceState(context);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
device.setState({ staticSessionId: invalidDeviceState });
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (invalidDeviceState) {
|
|
78
|
+
throw constants_1.ERRORS.TypedError('Device_InvalidState');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
device.setState({ sessionId: undefined });
|
|
83
|
+
return Promise.reject(error);
|
|
84
|
+
}
|
|
85
|
+
if (!isDeviceUnlocked && device.features.unlocked) {
|
|
86
|
+
method.postMessage((0, events_1.createDeviceMessage)(events_1.DEVICE.CHANGED, device.toMessageObject()));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
exports.validateState = validateState;
|
|
90
|
+
//# sourceMappingURL=validateState.js.map
|