Skip to content

Commit 5b3d4d1

Browse files
committed
feat(transport-bluetooth): use write with response
1 parent 62c7bc4 commit 5b3d4d1

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

packages/transport-bluetooth/src/client/bluetooth-api.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ import { PathInternal } from '@trezor/transport/src/types';
88
import { readMessageBuffer } from '@trezor/transport/src/utils/readMessageBuffer';
99

1010
import { TrezorBluetooth } from './trezor-bluetooth';
11-
import { BluetoothDevice } from './types';
11+
import { BluetoothDevice, TrezorBluetoothSettings } from './types';
1212

1313
// implementation of @trezor/transport/src/api/abstract
1414

15-
type BluetoothApiParams = AbstractApiConstructorParams & {
16-
url: string;
17-
};
15+
type BluetoothApiParams = AbstractApiConstructorParams & TrezorBluetoothSettings;
1816

1917
export class BluetoothApi extends AbstractApi {
2018
chunkSize = 244;
@@ -24,7 +22,7 @@ export class BluetoothApi extends AbstractApi {
2422
constructor(options: BluetoothApiParams) {
2523
super(options);
2624

27-
this.api = new TrezorBluetooth({ url: options.url, logger: options.logger });
25+
this.api = new TrezorBluetooth(options);
2826
}
2927

3028
private devicesToDescriptors(devices: BluetoothDevice[]) {

packages/transport-bluetooth/src/client/transport.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,21 @@ import { AbstractTransportParams } from '@trezor/transport/src/transports/abstra
22
import { AbstractApiTransport } from '@trezor/transport/src/transports/abstractApi';
33

44
import { BluetoothApi } from './bluetooth-api';
5+
import { TrezorBluetoothSettings } from './types';
56

67
// implementation of @trezor/transport/src/transports/abstractApi
78

8-
type BluetoothTransportParams = Omit<AbstractTransportParams, 'api'> & {
9-
url: string;
10-
};
9+
type BluetoothTransportParams = Omit<AbstractTransportParams, 'api'> & TrezorBluetoothSettings;
1110

1211
export class BluetoothTransport extends AbstractApiTransport {
1312
public name = 'BluetoothTransport' as const;
1413
public apiType = 'bluetooth' as const;
1514
private wsApi: BluetoothApi;
1615

1716
constructor(params: BluetoothTransportParams) {
18-
const { url, logger, ...rest } = params;
17+
const { url, logger, writeWithResponse, writeWithDelay, ...rest } = params;
1918

20-
const api = new BluetoothApi({ url, logger });
19+
const api = new BluetoothApi({ url, logger, writeWithResponse, writeWithDelay });
2120
api.on('transport-interface-error', ({ error }) => {
2221
this.emit('transport-error', error);
2322
});

packages/transport-bluetooth/src/client/trezor-bluetooth.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { resolveAfter } from '@trezor/utils';
12
import { WebsocketClient } from '@trezor/websocket-client';
23

34
import {
@@ -67,8 +68,42 @@ export class TrezorBluetooth extends WebsocketClient<NotificationEvent> {
6768
return this.sendMessage({ method: 'connect_device', params }, { timeout: wsTimeout });
6869
}
6970

70-
write(params: WriteParams) {
71-
const withResponse = false;
71+
private writeBuffer: Record<string, { bytes: number; lastWrite: number } | undefined> = {};
72+
73+
private async write(params: WriteParams) {
74+
const writeBuffer = this.writeBuffer[params.id];
75+
if (!writeBuffer) {
76+
throw new Error('Device not opened');
77+
}
78+
79+
// MacOS and Windows writeWithoutResponse sends packets faster than Trezor is able to process or just loose packets and doesn't send them at all (macos)
80+
// we need to use throttle it to make sure that they are received and processed correctly
81+
// NOTE: the faster it sends the longer it waits when withResponse is used
82+
const MIN_PACKETS = 8;
83+
const MAX_PACKETS = 16;
84+
const PACKET_SIZE = 244;
85+
const WITH_RESPONSE_BYTES = PACKET_SIZE * MAX_PACKETS;
86+
let withResponse = false;
87+
const lastWrite = Date.now() - writeBuffer.lastWrite;
88+
// windows: use delay between packets. do not use writeWithResponse - it takes too long
89+
if (this.settings.writeWithDelay && lastWrite < MIN_PACKETS) {
90+
const wait =
91+
MIN_PACKETS -
92+
lastWrite +
93+
Math.floor(MIN_PACKETS * ((writeBuffer.bytes / WITH_RESPONSE_BYTES) % 1));
94+
await resolveAfter(wait);
95+
}
96+
97+
// macos: use writeWithResponse occasionally (first few packets and every 16-nth)
98+
if (this.settings.writeWithResponse) {
99+
const sent = writeBuffer.bytes / PACKET_SIZE;
100+
if (sent <= MIN_PACKETS || sent % WITH_RESPONSE_BYTES === 0) {
101+
withResponse = true;
102+
}
103+
}
104+
105+
writeBuffer.lastWrite = Date.now();
106+
writeBuffer.bytes += params.data.length;
72107

73108
return this.sendMessage({ method: 'write', params: { ...params, withResponse } });
74109
}
@@ -94,6 +129,17 @@ export class TrezorBluetooth extends WebsocketClient<NotificationEvent> {
94129
return this.write(params);
95130
}
96131

132+
if (method === 'open_device') {
133+
this.writeBuffer[params] = {
134+
bytes: 0,
135+
lastWrite: 0,
136+
};
137+
}
138+
139+
if (method === 'close_device') {
140+
delete this.writeBuffer[params];
141+
}
142+
97143
return this.sendMessage({ method, params });
98144
}
99145

packages/transport-bluetooth/src/client/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export interface TrezorBluetoothSettings {
77
url: string;
88
logger?: Logger;
99
timeout?: number;
10+
writeWithResponse?: boolean;
11+
writeWithDelay?: boolean;
1012
}
1113

1214
export type BluetoothInfo = {

0 commit comments

Comments
 (0)