chore(protocol): distinguish DeviceThpState and ThpChannelState

This commit is contained in:
Szymon Lesisz
2025-11-27 14:05:39 +01:00
committed by Szymon Lesisz
parent e24696f40c
commit 780e5327f4
12 changed files with 37 additions and 33 deletions

View File

@@ -45,6 +45,7 @@ import {
DeviceFirmwareStatus,
DeviceState,
DeviceStatus,
DeviceThpState,
Device as DeviceTyped,
DeviceUniquePath,
Features,
@@ -1126,6 +1127,19 @@ export class Device extends TypedEmitter<DeviceEvents> {
return 'available';
}
private getDeviceThp(): DeviceThpState | undefined {
const state = this.thp?.serialize();
return state
? {
properties: state.properties,
credentials: state.credentials,
channel: state.channel,
}
: undefined;
}
// simplified object to pass via postMessage
toMessageObject(): DeviceTyped {
const { name, uniquePath: path, descriptor } = this;
@@ -1158,7 +1172,7 @@ export class Device extends TypedEmitter<DeviceEvents> {
name: this.name,
transportSessionOwner: this.sessionAcquired ? undefined : sessionOwner,
bluetoothProps,
thp: this.thp?.serialize(),
thp: this.getDeviceThp(),
status: this.busy ? this.busy : undefined,
};
}
@@ -1184,7 +1198,7 @@ export class Device extends TypedEmitter<DeviceEvents> {
availableTranslations: this.availableTranslations,
authenticityChecks: this.authenticityChecks,
bluetoothProps,
thp: this.thp?.serialize(),
thp: this.getDeviceThp(),
};
}
}

View File

@@ -75,6 +75,12 @@ export type DeviceState = {
deriveCardano?: boolean;
};
export type DeviceThpState = {
properties?: ThpStateSerialized['properties'];
credentials: ThpStateSerialized['credentials'];
channel: string;
};
// NOTE: unavailableCapabilities is an object with information what is NOT supported by this device.
// in ideal/expected setup this object should be empty but given setup might have exceptions.
// key = coin shortcut lowercase (ex: btc, eth, xrp) OR field declared in coins.json "supportedFirmware.capability"
@@ -159,7 +165,7 @@ export type KnownDevice = BaseDevice & {
_state?: DeviceState; // TODO: breaking change in next major release
state?: DeviceState['staticSessionId'];
features: PROTO.Features;
thp?: ThpStateSerialized;
thp?: DeviceThpState;
unavailableCapabilities: UnavailableCapabilities;
availableTranslations: Record<string, string>;
authenticityChecks: {
@@ -182,7 +188,7 @@ export type UnknownDevice = BaseDevice & {
id?: typeof undefined;
error?: typeof undefined;
features?: typeof undefined;
thp?: ThpStateSerialized;
thp?: DeviceThpState;
firmware?: typeof undefined;
firmwareReleaseConfigInfo?: typeof undefined;
firmwareType?: typeof undefined;

View File

@@ -9,6 +9,9 @@ import {
export type ThpStateSerialized = {
properties?: ThpDeviceProperties;
credentials: ThpCredentials[];
} & ThpChannelState;
export type ThpChannelState = {
channel: string; // 2 bytes as hex
sendBit: ThpMessageSyncBit; // host synchronization bit
recvBit: ThpMessageSyncBit; // device synchronization bit
@@ -272,7 +275,7 @@ export class ThpState {
};
}
deserialize(json: ReturnType<(typeof this)['serialize']>) {
deserialize(json: ThpChannelState) {
// simple fields validation
const error = new Error('ThpState.deserialize invalid state');
if (!json || typeof json !== 'object') {

View File

@@ -3,7 +3,7 @@ export type {
ThpPairingMethod,
ThpCredentials,
} from './protocol-thp/messages';
export type { ThpState, ThpStateSerialized } from './protocol-thp/ThpState';
export type { ThpState, ThpStateSerialized, ThpChannelState } from './protocol-thp/ThpState';
export type TransportProtocolDecode = (bytes: Buffer) => {
header: Buffer;

View File

@@ -508,15 +508,9 @@ const changed: Fixture<ReturnType<typeof deviceActions.deviceChanged>>[] = [
type: 'unacquired',
path: '2',
thp: {
properties: undefined,
channel: '00',
credentials: [],
expectedResponses: [],
recvBit: 0,
recvNonce: 0,
recvAckBit: 0,
sendBit: 0,
sendAckBit: 0,
sendNonce: 0,
},
}),
},

View File

@@ -1,7 +1,7 @@
import { Messages } from '@trezor/protobuf';
import {
PROTOCOL_MALFORMED,
ThpStateSerialized,
ThpChannelState,
TransportProtocol,
thp as protocolThp,
} from '@trezor/protocol';
@@ -30,7 +30,7 @@ export type AbortableParam = { signal?: AbortSignal; timeout?: number };
export type BridgeProtocolMessage = {
data: string;
protocol?: TransportProtocol['name'];
thpState?: ThpStateSerialized;
thpState?: ThpChannelState;
};
export type MessageResponse = Messages.MessageResponse | protocolThp.ThpMessageResponse;

View File

@@ -1,4 +1,4 @@
import type { ThpStateSerialized, TransportProtocol } from '@trezor/protocol';
import type { ThpChannelState, TransportProtocol } from '@trezor/protocol';
import type { BridgeProtocolMessage } from '../types';
@@ -55,7 +55,7 @@ export function validateProtocolMessage(body: unknown, withData = true): BridgeP
export function createProtocolMessage(
body: unknown,
protocol?: TransportProtocol | TransportProtocol['name'],
thpState?: ThpStateSerialized,
thpState?: ThpChannelState,
) {
let data;
if (Buffer.isBuffer(body)) {

View File

@@ -22,7 +22,6 @@
"@trezor/connect": "workspace:*",
"@trezor/device-utils": "workspace:*",
"@trezor/env-utils": "workspace:*",
"@trezor/protocol": "workspace:*",
"@trezor/type-utils": "workspace:*",
"@trezor/urls": "workspace:*",
"@trezor/utils": "workspace:*",

View File

@@ -10,7 +10,6 @@ import {
UnavailableCapability,
} from '@trezor/connect';
import { DeviceModelInternal, getNarrowedDeviceModelInternal } from '@trezor/device-utils';
import { ThpStateSerialized } from '@trezor/protocol';
import { exhaustive } from '@trezor/type-utils';
import * as URLS from '@trezor/urls';
import { isArrayMember } from '@trezor/utils';
@@ -524,7 +523,7 @@ export const getDeviceInternalModel = (
export const getIsThpDevice = <T extends Device | TrezorDevice>(
device: T,
): device is T & { thp: ThpStateSerialized } => device.thp !== undefined;
): device is T & { thp: NonNullable<Device['thp']> } => device.thp !== undefined;
export const getIsDeviceInitialized = ({
deviceMode,

View File

@@ -12,7 +12,6 @@
{ "path": "../../packages/connect" },
{ "path": "../../packages/device-utils" },
{ "path": "../../packages/env-utils" },
{ "path": "../../packages/protocol" },
{ "path": "../../packages/type-utils" },
{ "path": "../../packages/urls" },
{ "path": "../../packages/utils" }

View File

@@ -1,6 +1,6 @@
import type { ThpSuiteCredentials } from '@suite-common/suite-types';
import type { DeviceThpState } from '@trezor/connect';
import { DeviceModelInternal } from '@trezor/device-utils';
import type { ThpStateSerialized } from '@trezor/protocol';
// This file is intentionally not reexported in index.ts, so that bundler won't have to import.
@@ -20,9 +20,7 @@ export const createCredential = (
/**
* Generate a mock device.thp properties as they are on a readable device
*/
export const createDeviceThp = (
partialDeviceThp?: Partial<ThpStateSerialized>,
): ThpStateSerialized => ({
export const createDeviceThp = (partialDeviceThp?: Partial<DeviceThpState>): DeviceThpState => ({
properties: {
internal_model: DeviceModelInternal.T3W1,
model_variant: 0,
@@ -31,13 +29,6 @@ export const createDeviceThp = (
pairing_methods: [],
},
channel: 'channel-id',
sendBit: 0,
recvBit: 0,
sendAckBit: 0,
recvAckBit: 0,
sendNonce: 1,
recvNonce: 2,
expectedResponses: [1],
credentials: [createCredential()],
...partialDeviceThp,
});

View File

@@ -10843,7 +10843,6 @@ __metadata:
"@trezor/connect": "workspace:*"
"@trezor/device-utils": "workspace:^"
"@trezor/env-utils": "workspace:*"
"@trezor/protocol": "workspace:*"
"@trezor/type-utils": "workspace:*"
"@trezor/urls": "workspace:*"
"@trezor/utils": "workspace:*"