diff --git a/.github/workflows/template-connect-test-params.yml b/.github/workflows/template-connect-test-params.yml index c95e9eec5ae..e2093c0d708 100644 --- a/.github/workflows/template-connect-test-params.yml +++ b/.github/workflows/template-connect-test-params.yml @@ -57,6 +57,7 @@ jobs: # todo: ideally do not install everything. possibly only devDependencies could be enough for testing (if there was not for building libs)? - run: sed -i "/\"node\"/d" package.json - run: yarn install + - run: yarn workspace @trezor/transport-bridge build:js # nightly test - run without cached txs - if: ${{ github.event_name == 'schedule' }} run: echo "ADDITIONAL_ARGS=-c" >> "$GITHUB_ENV" diff --git a/docker/docker-compose.connect-test.yml b/docker/docker-compose.connect-test.yml index 2c4a44bdec6..410290edf00 100644 --- a/docker/docker-compose.connect-test.yml +++ b/docker/docker-compose.connect-test.yml @@ -1,8 +1,10 @@ version: "3.9" services: trezor-user-env-unix: - image: ghcr.io/trezor/trezor-user-env:766f7d2cd13635fbdb61afa569f6aeb927085bf1 + image: ghcr.io/trezor/trezor-user-env:d029907396738bfff46cc58990aa503140f95f9a@sha256:f83c29a9b882aaf530c7b6f07aafabd650299213d438a70e93bcc4f4d634901e environment: - SDL_VIDEODRIVER=dummy - XDG_RUNTIME_DIR=/var/tmp + volumes: + - ../:/trezor-suite network_mode: host diff --git a/packages/connect/e2e/__fixtures__/signTransactionDecred.ts b/packages/connect/e2e/__fixtures__/signTransactionDecred.ts index 23d2383023f..98abbcf9360 100644 --- a/packages/connect/e2e/__fixtures__/signTransactionDecred.ts +++ b/packages/connect/e2e/__fixtures__/signTransactionDecred.ts @@ -194,7 +194,7 @@ export default { }, { // https://github.com/trezor/trezor-firmware/pull/2703/ - rules: ['<2.8.2'], + rules: ['<2.9.9'], success: false, }, ], diff --git a/packages/connect/e2e/common.setup.ts b/packages/connect/e2e/common.setup.ts index 7b2c951c210..0abb189d1fc 100644 --- a/packages/connect/e2e/common.setup.ts +++ b/packages/connect/e2e/common.setup.ts @@ -78,7 +78,8 @@ export const setup = async ( TrezorUserEnvLink.state = options; // after all is done, start bridge again - await TrezorUserEnvLink.startBridge(); + await TrezorUserEnvLink.startBridge('node-bridge'); + // await TrezorUserEnvLink.startBridge(); }; export const initTrezorConnect = async ( @@ -114,16 +115,30 @@ export const initTrezorConnect = async ( setTimeout(() => TrezorUserEnvLink.send({ type: 'emulator-press-yes' }), 1); }); + // TrezorConnect.on('ui-request_passphrase', () => { + // TrezorConnect.uiResponse({ + // type: 'ui-receive_passphrase', + // payload: { value: '' }, + // }); + // }); + await TrezorConnect.init({ manifest: { appUrl: 'tests.connect.trezor.io', email: 'tests@connect.trezor.io', }, transports: ['BridgeTransport'], + // transports: ['UdpTransport'], debug: false, popup: false, pendingTransportEvent: true, connectSrc: process.env.TREZOR_CONNECT_SRC, // custom source for karma tests + thp: { + hostName: 'TrezorConnect', + staticKeys: '0007070707070707070707070707070707070707070707070707070707070747', + knownCredentials: [], + pairingMethods: ['NoMethod'] as any, + }, ...options, }); }; diff --git a/packages/connect/e2e/tests/device/thpPairing.test.ts b/packages/connect/e2e/tests/device/thpPairing.test.ts new file mode 100644 index 00000000000..2ca7b3b9484 --- /dev/null +++ b/packages/connect/e2e/tests/device/thpPairing.test.ts @@ -0,0 +1,142 @@ +import { getController, initTrezorConnect, setup } from '../../common.setup'; +import TrezorConnect, { ConnectSettings, Device } from '../../../src'; + +describe('THP pairing', () => { + const controller = getController(); + + beforeAll(async () => { + await setup(controller, { mnemonic: 'mnemonic_all' }); + }); + + afterEach(() => { + TrezorConnect.dispose(); + }); + + const waitForDevice = async (settings: Partial) => { + await initTrezorConnect(controller, { + thp: { + hostName: 'TrezorConnect', + staticKeys: '0007070707070707070707070707070707070707070707070707070707070747', + knownCredentials: [], + pairingMethods: [], + ...settings, + }, + }); + + return new Promise((resolve, reject) => { + const onDeviceConnected = (device: Device) => { + TrezorConnect.removeAllListeners('device-connect'); + TrezorConnect.removeAllListeners('device-connect_unacquired'); + if (device.type === 'unreadable') { + reject(new Error('Device unreadable')); + } + resolve(device); + }; + TrezorConnect.on('device-connect', onDeviceConnected); + TrezorConnect.on('device-connect_unacquired', onDeviceConnected); + }); + }; + + it('ThpPairing NoMethod', async () => { + await waitForDevice({ pairingMethods: ['NoMethod'] }); + + const address = await TrezorConnect.getAddress({ + // device, + path: "m/44'/0'/0'/1/1", + showOnTrezor: true, + }); + expect(address).toMatchObject({ success: true }); + }); + + it('ThpPairing CodeEntry', async () => { + const device = await waitForDevice({ pairingMethods: ['CodeEntry'] }); + + TrezorConnect.on('ui-request_thp_pairing', async ({ device }) => { + const state = await controller.getDebugState(device.protocolState.channel); + TrezorConnect.removeAllListeners('ui-request_thp_pairing'); + TrezorConnect.uiResponse({ + type: 'ui-receive_thp_pairing_tag', + payload: { + source: 'code-entry', + value: state.thp_pairing_code_entry_code.toString(), + }, + }); + }); + + const address = await TrezorConnect.getAddress({ + device, + path: "m/44'/0'/0'/1/1", + // showOnTrezor: true, + }); + expect(address).toMatchObject({ success: true }); + }); + + it('ThpPairing QrCode', async () => { + const device = await waitForDevice({ pairingMethods: ['QrCode'] }); + + TrezorConnect.on('ui-request_thp_pairing', async ({ device }) => { + const state = await controller.getDebugState(device.protocolState.channel); + TrezorConnect.removeAllListeners('ui-request_thp_pairing'); + TrezorConnect.uiResponse({ + type: 'ui-receive_thp_pairing_tag', + payload: { source: 'qr-code', value: state.thp_pairing_code_qr_code }, + }); + }); + + const address = await TrezorConnect.getAddress({ + device, + path: "m/44'/0'/0'/1/1", + // showOnTrezor: true, + }); + expect(address).toMatchObject({ success: true }); + }); + + it('ThpPairing NFC_Unidirectional', async () => { + const device = await waitForDevice({ pairingMethods: ['NFC_Unidirectional'] }); + + TrezorConnect.on('ui-request_thp_pairing', async ({ device }) => { + // await new Promise(resolve => setTimeout(resolve, 1000)); + const state = await controller.getDebugState(device.protocolState.channel); + TrezorConnect.removeAllListeners('ui-request_thp_pairing'); + TrezorConnect.uiResponse({ + type: 'ui-receive_thp_pairing_tag', + payload: { source: 'nfc', value: state.thp_pairing_code_nfc_unidirectional }, + }); + }); + + const address = await TrezorConnect.getAddress({ + device, + path: "m/44'/0'/0'/1/1", + // showOnTrezor: true, + }); + expect(address).toMatchObject({ success: true }); + }); + + it('ThpPairing no matching method. device unreadable', async () => { + const device = await waitForDevice({ + pairingMethods: ['FooBar', undefined, 1234, null, {}] as any, + }); + expect(device.type).toEqual('unreadable'); + }); + + it('ThpPairing using known credentials', async () => { + const device = await waitForDevice({ + pairingMethods: ['CodeEntry'], + knownCredentials: [ + { + trezor_static_pubkey: + '38d6437ef1d67a4742265281de1e9a68df28774636f34b5e3e336d3ab90e671c', + credential: + '0a0f0a0d5472657a6f72436f6e6e6563741220f53793b13dffe2a4f01c2c7272aecc75b8596cf0fce4b09efd4fb353696a179b', + }, + ], + }); + + const address = await TrezorConnect.getAddress({ + device, + path: "m/44'/0'/0'/1/1", + showOnTrezor: true, + }); + expect(address).toMatchObject({ success: true }); + }); +}); diff --git a/packages/trezor-user-env-link/src/api.ts b/packages/trezor-user-env-link/src/api.ts index c2a9616b695..bceb6773cac 100644 --- a/packages/trezor-user-env-link/src/api.ts +++ b/packages/trezor-user-env-link/src/api.ts @@ -270,8 +270,12 @@ export class TrezorUserEnvLinkClass extends TypedEmitter return null; } - async getDebugState() { - const { response } = await this.client.send({ type: 'emulator-get-debug-state' }); + + async getDebugState(thp_channel_id?: string) { + const { response } = await this.client.send({ + type: 'emulator-get-debug-state', + thp_channel_id, + }); return response; } diff --git a/scripts/ci/connect-test-matrix-generator.js b/scripts/ci/connect-test-matrix-generator.js index a8640c4e0a9..dc1e535c728 100644 --- a/scripts/ci/connect-test-matrix-generator.js +++ b/scripts/ci/connect-test-matrix-generator.js @@ -4,7 +4,7 @@ const groups = { api: { name: 'api', pattern: - 'init authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy override checkFirmwareAuthenticity keepSession cancel.test info.test', + 'init authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy override checkFirmwareAuthenticity keepSession cancel.test info.test thpPairing', methods: '', }, management: {