Skip to content

Commit

Permalink
wip(connect): core mode suite-desktop-core
Browse files Browse the repository at this point in the history
  • Loading branch information
mroz22 committed Oct 28, 2024
1 parent 1baefa1 commit 6aa7d61
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export const init =
// Get default coreMode from URL params (?core-mode=auto)
const urlParams = new URLSearchParams(window.location.search);
const coreMode = (urlParams.get('core-mode') as ConnectOptions['coreMode']) || 'auto';

// const coreMode: ConnectOptions['coreMode'] = 'suite-desktop';
const connectOptions = {
coreMode,
transportReconnect: true,
Expand All @@ -158,6 +158,7 @@ export const init =
},
trustedHost: false,
connectSrc: window.__TREZOR_CONNECT_SRC,

...options,
};

Expand Down
1 change: 1 addition & 0 deletions packages/connect-explorer/src/components/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const Settings = () => {
{ value: 'iframe', label: 'Iframe' },
{ value: 'popup', label: 'Popup' },
...(isBetaOnly ? [{ value: 'deeplink', label: 'Deeplink (mobile)' }] : []),
...(isBetaOnly ? [{ value: 'suite-desktop', label: 'Suite desktop' }] : []),
],
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/connect-web/src/impl/core-in-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// todo:
174 changes: 174 additions & 0 deletions packages/connect-web/src/impl/core-in-suite-desktop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import EventEmitter from 'events';

// NOTE: @trezor/connect part is intentionally not imported from the index so we do include the whole library.
import {
IFRAME,
UiResponseEvent,
CallMethodPayload,
CallMethodAnyResponse,
} from '@trezor/connect/src/events';
import * as ERRORS from '@trezor/connect/src/constants/errors';
import type {
ConnectSettings,
ConnectSettingsPublic,
ConnectSettingsWeb,
Manifest,
Response,
} from '@trezor/connect/src/types';
import { ConnectFactoryDependencies, factory } from '@trezor/connect/src/factory';

import { parseConnectSettings } from '../connectSettings';
import { Login } from '@trezor/connect/src/types/api/requestLogin';
// import { createDeferred } from '@trezor/utils';

/**
* Base class for CoreInPopup methods for TrezorConnect factory.
* This implementation is directly used here in connect-web, but it is also extended in connect-webextension.
*/
export class CoreInSuiteDesktop implements ConnectFactoryDependencies<ConnectSettingsWeb> {
public eventEmitter = new EventEmitter();
protected _settings: ConnectSettings;
private ws?: WebSocket;

public constructor() {
this._settings = parseConnectSettings();
}

public manifest(data: Manifest) {
this._settings = parseConnectSettings({
...this._settings,
manifest: data,
});
}

public dispose() {
this.eventEmitter.removeAllListeners();
this._settings = parseConnectSettings();

return Promise.resolve(undefined);
}

public cancel(_error?: string) {}

private async handshake() {

Check failure on line 53 in packages/connect-web/src/impl/core-in-suite-desktop.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Async method 'handshake' has no 'await' expression
console.log('init ==== waiting for handshake, this.ws?.readyState', this.ws?.readyState);

return Promise.race([
new Promise<void>(resolve => {
const listener = (event: WebSocketEventMap['message']) => {
console.log('init ==== received: %s', event.data);
if (event.data === 'handshake') {
resolve();
removeEventListener('message', listener);
}
};
this.ws?.addEventListener('message', listener);
console.log('sending handshake');
this.ws?.send('handshake');
}),
new Promise<void>((_resolve, reject) => {
setTimeout(() => {
reject(new Error('Handshake timeout'));
}, 1000);
}),
]);
}
public async init(settings: Partial<ConnectSettingsPublic> = {}): Promise<void> {
const newSettings = parseConnectSettings({
...this._settings,
...settings,
});

// defaults
if (!newSettings.transports?.length) {
newSettings.transports = ['BridgeTransport', 'WebUsbTransport'];
}
this._settings = newSettings;

this.ws = new WebSocket('ws://localhost:8090');
this.ws.addEventListener('error', console.error);

await new Promise(resolve => {
this.ws?.addEventListener('opened', resolve);
});
return this.handshake();

Check failure on line 94 in packages/connect-web/src/impl/core-in-suite-desktop.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Expected blank line before this statement
}

/**
* 1. opens popup
* 2. sends request to popup where the request is handled by core
* 3. returns response
*/
public async call(params: CallMethodPayload): Promise<CallMethodAnyResponse> {
try {
await this.handshake();

this.ws?.send(
JSON.stringify({
type: IFRAME.CALL,
payload: params,
}),
);

return new Promise(resolve => {
const listener = (event: WebSocketEventMap['message']) => {
console.log('received: %s', event.data);
try {
resolve(JSON.parse(event.data));
} catch (err) {
console.log('=== err, err');
console.log('received something weird', event.data);
}
this.ws?.removeEventListener('message', listener);
};

this.ws?.addEventListener('message', listener);
});
} catch (err) {
console.log('---call err', err);
return {

Check failure on line 129 in packages/connect-web/src/impl/core-in-suite-desktop.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

Expected blank line before this statement
success: false,
payload: {
error: err.message,
},
};
}
}

uiResponse(_response: UiResponseEvent) {
// this shouldn't be needed, ui response should be handled in suite-desktop
throw ERRORS.TypedError('Method_InvalidPackage');
}

renderWebUSBButton() {}

requestLogin(): Response<Login> {
// todo: not supported yet
throw ERRORS.TypedError('Method_InvalidPackage');
}

disableWebUSB() {
// todo: not supported yet, probably not needed
throw ERRORS.TypedError('Method_InvalidPackage');
}

requestWebUSBDevice() {
// not needed - webusb pairing happens in popup
throw ERRORS.TypedError('Method_InvalidPackage');
}
}

const impl = new CoreInSuiteDesktop();

// Exported to enable using directly
export const TrezorConnect = factory({
// Bind all methods due to shadowing `this`
eventEmitter: impl.eventEmitter,
init: impl.init.bind(impl),
call: impl.call.bind(impl),
manifest: impl.manifest.bind(impl),
requestLogin: impl.requestLogin.bind(impl),
uiResponse: impl.uiResponse.bind(impl),
cancel: impl.cancel.bind(impl),
dispose: impl.dispose.bind(impl),
});
10 changes: 9 additions & 1 deletion packages/connect-web/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ConnectFactoryDependencies, factory } from '@trezor/connect/src/factory
import { TrezorConnectDynamic } from '@trezor/connect/src/impl/dynamic';
import { CoreInIframe } from './impl/core-in-iframe';
import { CoreInPopup } from './impl/core-in-popup';
import { CoreInSuiteDesktop } from './impl/core-in-suite-desktop';
import type { ConnectSettingsPublic, ConnectSettingsWeb } from '@trezor/connect';
import { getEnv } from './connectSettings';

Expand All @@ -14,7 +15,7 @@ type ConnectWebExtraMethods = {
};

const impl = new TrezorConnectDynamic<
'iframe' | 'core-in-popup',
'iframe' | 'core-in-popup' | 'core-in-suite-desktop',
ConnectSettingsWeb,
ConnectFactoryDependencies<ConnectSettingsWeb> & ConnectWebExtraMethods
>({
Expand All @@ -27,12 +28,19 @@ const impl = new TrezorConnectDynamic<
type: 'core-in-popup',
impl: new CoreInPopup(),
},
{
type: 'core-in-suite-desktop',
impl: new CoreInSuiteDesktop(),
},
],
getInitTarget: (settings: Partial<ConnectSettingsPublic & ConnectSettingsWeb>) => {
console.log(settings.coreMode);
if (settings.coreMode === 'iframe') {
return 'iframe';
} else if (settings.coreMode === 'popup') {
return 'core-in-popup';
} else if (settings.coreMode === 'suite-desktop') {
return 'core-in-suite-desktop';
} else {
if (settings.coreMode && settings.coreMode !== 'auto') {
console.warn(`Invalid coreMode: ${settings.coreMode}`);
Expand Down
2 changes: 1 addition & 1 deletion packages/connect/src/types/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export interface ConnectSettingsInternal {
export interface ConnectSettingsWeb {
hostLabel?: string;
hostIcon?: string;
coreMode?: 'auto' | 'popup' | 'iframe' | 'deeplink';
coreMode?: 'auto' | 'popup' | 'iframe' | 'deeplink' | 'suite-desktop';
}
export interface ConnectSettingsWebextension {
/** _extendWebextensionLifetime features makes the service worker in @trezor/connect-webextension stay alive longer */
Expand Down

0 comments on commit 6aa7d61

Please sign in to comment.