trezor/connect

correct way to issue commands to device

Closed this issue · 29 comments

hello, i'm using a completely local trezor ecosystem to learn more about how the technology works. at the moment, i'm trying to learn more about how trezor-connect works and i've decided to try and create a simple web wallet like that available at http://wallet.trezor.io

i'm currently using a trezor one device (physical; not emulated) and am running it against trezord-go on the default port. i've created an angular application (my custom web wallet) and have included trezor-connect in my project. i have the trezor-connect web service running on localhost and i have configured my custom web wallet to init with a manifest that targets localhost:8088. i am not using webusb nor am i using popups.

the software seems to initialized fine (debug logging to the console shows that transport and init do not fail) but here's where i run into problems. i have added a few buttons on a single angular component that simply fire different trezor-connect methods. i have one button for getFeatures() and another for resetDevice(). if i click the button to fire getFeatures(), i notice that it takes a long time to return any data from the device... upwards of 30 seconds. furthermore, if i issue a resetDevice() before a getFeatures() request has returned, i get the device call in progress message.

my questions are:

  • why do calls to the device take a long time to return?
  • how can I cancel a currently running call?
  • what is the correct way to handle interactive calls (things that require an action on the physical device itself) if i'm handling it all myself and do not want to use the supplied popup based ui provided by the trezor-connect service?

thanks in advance, my friends -- very cool stuff you are all building here

i am also noticing that even the initialization process takes a while (15+ seconds) occasionally. can anyone point me to up-to-date documentation that outlines the flow of information from a third-party web wallet to an end users device and back? i cannot find anything comprehensive. seems that most documentation is in pieces, all over the place, and largely out of date. i just want to be able to run my own private trezor lab here at home and would like to do so without relying on the ui served by the trezor-connect service (popups disabled). any help would be appreciated, here.

it's also worth noting that the network tab of devconsole shows long times for requests to /enumerate, /listen, and /acquire (all from the iframe, obviously). surely i'm missing something.

@szymonlesisz i wonder if you could help shed some light on this, my friend... how best can i use trezor connect with popup disabled and with only a single device? i believe the bulk of my issues are coming from the fact that i am not acquiring nor releasing my device correctly (also my angular app is restarting regularly which i understand will cause issues, too?).

i hate to bother but i cannot find any documentation nor any functional example code.

Hi @curve-25519,
I know that lack of documentation is painful, but i really don't have a time to write this.
I hope you are aware that your project will only work locally with popup disabled, because of CORS filter in trezord?
I could help if you share some code

totally understand! i realize you're probably busy so i'll do what i can to make this easiest for you.

i am simply trying to do the following:

TrezorConnect.init({
        connectSrc: 'http://localhost:8088/', // running my own connect backend
        popup: false,
        webusb: false,
        debug: false,
        manifest: {
            email: 'email@developer.com',
            appUrl: 'electron-app-boilerplate',
        },
    }).then(() => {
        console.log('initialized!'); // this does not log right away
    }).catch(error => {
        console.error('could not initialize:', error);
    });

TrezorConnect.getFeatures().then(features => {
    console.log(features); // this does not log right away
});

the problem is that both of those calls (init() and getFeatures()) take forever to return. i see in the bridge logs and in chrome dev-tools network monitoring that calls are being made to the endpoints: /enumerate, /acquire, and /listen and all of those seem slow to return.

why do calls to the device take a long time to resolve? also, if my angular application restarts in the middle of a call and that causes major delays while timeouts expire, how should i be handling that situation?

what do you mean by slow return? connects should be initialized in matter of seconds (3 - 4 max).
What's your trezord version? Have you tried it with webusb in chrome and got same results?
Is popup mode behaving the same (slow return)? for example here: https://trezor.github.io/connect-explorer/#/?
Do you call those two methods independently? Or waiting for "init" result and then calling "getFeatures"?

  1. calls take upwards of a minute to return data, including the init function (chrome shows calls to /enumerate /acquire and /listen all take the longest for a response)
  2. trezord is version 2.0.27
  3. i have not used webusb, no -- i will try this
  4. connect-explorer seems to function in good times -- a call to getFeatures() is nearly instant
  5. i dont start making subsequent calls until connect has initialized

note: i do get method not allowed for this configuration errors for most of the device configuration methods on connect-explorer. perhaps this is related?

no, it's not it, management methods are not allowed in popup mode at all, we don't want to provide a possibility to do some bad things (like wipe or recovery) in 3rd-party websites

At first sight i'm blaming trezord could you try with 2.0.29? from: https://github.com/trezor/webwallet-data/tree/master/bridge

you can also try to add pendingTransportEvent: false to init. This param disables "waiting for first device" process (/enumerate and /listen calls), it's useful when you already have some stored information about device

great, thank you! i'll try all this and get back to you. i really appreciate the help.

update:

ok, @szymonlesisz, i have tested with both 2.0.29 and 2.0.30 -- using the following simple code:

ngOnInit() {
    let config = {
      debug: false,
      lazyLoad: false,
      manifest: {
        email: 'example@example.com',
        appUrl: 'https://example.com'
      },
      popup: false,
      webusb: false,
      pendingTransportEvent: false
    };

    console.log('start', Date.now())

    TrezorConnect.init(config).then(() => {
      console.log('stop', Date.now());
    }).catch(err => console.log(err));
  }

  getFeatures() {
    TrezorConnect.getFeatures().then(features => console.log(features));
  }

ngOnInit is called when angular initializes that component for the first time. it is there that i initialize trezor connect with a very simple settings object. i have a button in my view that calls getFeatures and i am clicking it once the stop timestamp is logged to the console.

the behavior i am seeing is that the init is actually happening pretty fast. 2-3 seconds it looks like. but as soon as it initializes, i click my button to get features of the device and that call takes more than a minute to log to my console.

as an additional test, i created a very simple web app to eliminate any weird angular fuckery that may be happening. i am using trezord version 2.0.29 and have created two files:

index.html

<html>
    <body>
        <button id="btn">get features</button>
        <script src="https://connect.trezor.io/8/trezor-connect.js"></script>
        <script src="index.js"></script>
    </body>
</html>

and index.js

'use strict';

const TrezorConnect = window.TrezorConnect;
const settings = {
    debug: false,
    lazyLoad: false,
    manifest: {
      email: 'example@example.com',
      appUrl: 'https://example.com'
    },
    popup: false,
    webusb: false,
    pendingTransportEvent: false
};

const btn = document.getElementById('btn');
btn.hidden = true;

function init() {
    TrezorConnect.init(settings).then(() => {
        btn.hidden = false;
    });
}

function click() {
    TrezorConnect.getFeatures().then(results => {
        console.log(results); // why does this take forever to return!?!
    });
}

btn.addEventListener('click', click);
window.onload = init;

again, the init process is returning almost immediately now (great!) but the getFeatures call is taking more than a minute (two minutes, actually!).

what about webusb, have you tried it (in chrome, with pendingTransportEvent: true and lazyLoading: false)
have you also tried it in different browser?
Its strange because your code looks legit and should be working fine

are you running your project from http or file protocol?

I've just tried your example and the results are:

  • from file:// protocol - doesnt work with popup: false, works with popup true (maybe it should be allowed to not using popup from "file://")
  • from http:// protocol works like it should

Whats your setup? system? browser? looks like some weird edgecase to me

i am using a node package called http-server to serve the directory that contains those two files. so yes, i'm using http. i develop on a fedora workstation 32 virtual machine running firefox and chromium web browsers. this definitely sounds like an edge case if no one else is having these types of problems... whats curious, however, is that it does work for me albeit slowly.

and if you enable popup: true it also runs slowly? (i know you don't want to, i'm just curious)

yep! it does popup the trezor popup correctly but it spins and spins for about two minutes before finally closing and then logging out my device features.

ok, another try:

additional questions:

  • are you using emulator or real device?
  • how do you run trezord ?

however if it works correctly in connect-explorer (doesnt spin forever in popup) but it doesnt in your local project it means that the problem is not in the connect but in your project configuration

have you tried to run your simple example with popup: true from file:// protocol? does it also hungs?

i was actually running a local backend when i opened this issue originally. the problem definitely occurs there, too. i can get those logs to you, no problem, though.

to answer your questions:

  1. real device (trezor one)
  2. sudo ./trezord-go after having built it using go install && go build from the project root directory

maybe useful but here are the stdout logs from trezord-go from startup to the point at which a getFeatures request returned successfully:

λ sudo ./trezord-go-29
[sudo] password for user: 
2020/07/01 03:27:57 trezord v2.0.29 is starting.
POST /
127.0.0.1 - - [01/Jul/2020:03:28:05 -0600] "POST / HTTP/1.1" 200 21
POST /enumerate
127.0.0.1 - - [01/Jul/2020:03:28:05 -0600] "POST /enumerate HTTP/1.1" 200 94
POST /listen
POST /acquire/1/null
127.0.0.1 - - [01/Jul/2020:03:28:05 -0600] "POST /acquire/1/null HTTP/1.1" 200 16
127.0.0.1 - - [01/Jul/2020:03:28:05 -0600] "POST /listen HTTP/1.1" 200 93
POST /call/1
POST /listen
127.0.0.1 - - [01/Jul/2020:03:29:10 -0600] "POST /call/1 HTTP/1.1" 200 338
POST /release/1
127.0.0.1 - - [01/Jul/2020:03:29:10 -0600] "POST /release/1 HTTP/1.1" 200 16
127.0.0.1 - - [01/Jul/2020:03:29:10 -0600] "POST /listen HTTP/1.1" 200 94
POST /listen
POST /acquire/1/null
127.0.0.1 - - [01/Jul/2020:03:29:10 -0600] "POST /acquire/1/null HTTP/1.1" 200 16
127.0.0.1 - - [01/Jul/2020:03:29:10 -0600] "POST /listen HTTP/1.1" 200 93
POST /call/2
POST /listen
127.0.0.1 - - [01/Jul/2020:03:30:15 -0600] "POST /call/2 HTTP/1.1" 200 408
POST /call/2
127.0.0.1 - - [01/Jul/2020:03:30:15 -0600] "POST /call/2 HTTP/1.1" 200 338
POST /release/2
127.0.0.1 - - [01/Jul/2020:03:30:15 -0600] "POST /release/2 HTTP/1.1" 200 16
127.0.0.1 - - [01/Jul/2020:03:30:15 -0600] "POST /listen HTTP/1.1" 200 94
POST /listen

just tested using file:// as well and the popup still takes a while. i also tested using a prebuilt trezord binary (x64 linux rpm) and double-checked to see if the udev rules were in place (which they were) but still i am having slowness.

and finally, the complete devtools log from a test using debug: true:

λ cat ~/trezor-connect.log 
DevTools failed to load SourceMap: Could not load content for https://connect.trezor.io/8/es6-promise.map: HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "UI_EVENT", type: "iframe-bootstrap", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "TRANSPORT_EVENT", type: "transport-start", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "UI_EVENT", type: "iframe-loaded", payload: undefined}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "UI_EVENT", type: "iframe-loaded", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 Core handle message in core false {event: undefined, type: "iframe-call", payload: {…}, id: 1}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: undefined, type: "popup-bootstrap", payload: undefined}
trezor-connect.js:4362 [trezor-connect.js] Undefined message undefined MessageEvent {isTrusted: true, data: {…}, origin: "https://connect.trezor.io", lastEventId: "", source: global, …}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "UI_EVENT", type: "popup-loaded", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "UI_EVENT", type: "popup-handshake", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 Core handle message in core true {event: "UI_EVENT", type: "popup-handshake", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "DEVICE_EVENT", type: "device-changed", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "DEVICE_EVENT", type: "device-changed", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "DEVICE_EVENT", type: "device-connect", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "DEVICE_EVENT", type: "device-changed", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "UI_EVENT", type: "ui-cancel-popup-request", payload: undefined}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "UI_EVENT", type: "ui-cancel-popup-request", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "DEVICE_EVENT", type: "device-changed", payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 Core onCall::finally {event: "RESPONSE_EVENT", type: "RESPONSE_EVENT", id: 1, success: true, payload: {…}}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "UI_EVENT", type: "ui-cancel-popup-request", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "UI_EVENT", type: "ui-close_window", payload: undefined}
iframe.bbceb1e3ea7426b79540.js:15 Core Cleanup...
iframe.bbceb1e3ea7426b79540.js:15 IFrame postMessage {event: "RESPONSE_EVENT", type: "RESPONSE_EVENT", id: 1, success: true, payload: {…}}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "UI_EVENT", type: "ui-cancel-popup-request", payload: undefined}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "UI_EVENT", type: "ui-close_window", payload: undefined}
trezor-connect.js:4362 [trezor-connect.js] handleMessage {event: "RESPONSE_EVENT", type: "RESPONSE_EVENT", payload: {…}, id: 1, success: true}
index.js:27 {id: 1, success: true, payload: {…}}
λ

summary:

  • connect-explorer is working without issue (hosted online) on the same machine
  • local project has delays in response from trezord in any possible configuration (project hosted on http://localhost or file://, connect hosted in trezor.io or localhost)

based on that looks more like trezord + your machine setup (virtualmachine? running trezord from sudo? browser? idk...)

To make sure try it with web usb:

  • add "PAIR" button to your html:
    <div class="trezor-webusb-button">PAIR</div>
  • enable webusb in settings
  • disable trezord
  • render webusb button:
TrezorConnect.init(settings).then(() => {
    TrezorConnect.renderWebUSBButton();
    btn.hidden = false;
});

before calling "getFeatures" you need to pair your device first (using PAIR button)

yes, that summary is correct! doing the webusb tests now but noticed a few things:

  1. trezor-link appears to still be attempting to communicate with the bridge even though i have webusb enabled in the config/settings -- is this normal?
  2. when i pair the device and try to run getFeatures, i get an error. troubleshooting this now.

edit: maybe ignore that error; that only happens when i include the event handling for ui-select_device as was suggested in one example I saw somewhere. when i remove that, the error goes away but now seems like it wont actually try to return the device features when i try.

edit2: yes, it appears that when i try to getFeatures after pairing, several ui events fire but nothing else. i see events for ui-request_window, popup-handshake, and ui-select_device. is it absolutely essential that i handle those explicitly?

I just realised that you will need to catch "pair" event, try with this example, note that it will not work on file:// protocol

<html>
    <body>
        <div id="btn" style="position: relative;" class="trezor-webusb-button">PAIR</div>
   
        <script src="https://connect.trezor.io/8/trezor-connect.js"></script>
        <script>
            const TrezorConnect = window.TrezorConnect;
            const settings = {
                debug: true,
                manifest: {
                    email: 'example@example.com',
                    appUrl: 'https://example.com'
                },
                popup: false,
                webusb: true,
            };

            TrezorConnect.on('ui-select_device', (event) => {
                if (event.devices.length > 0) {
                    // more then one device connected
                    // example how to respond to select device
                    TrezorConnect.uiResponse({ type: 'ui-receive_device', payload: {
                        device: event.devices[0]
                    }});
                } else {
                    // no devices connected, waiting for connection (pair)
                }
            });

            const btn = document.getElementById('btn');
            btn.hidden = true;

            function init() {
                TrezorConnect.init(settings).then(() => {
                    btn.hidden = false;
                    TrezorConnect.renderWebUSBButton();
                    TrezorConnect.getFeatures().then(results => {
                        console.log(results); // why does this take forever to return!?!
                    });
                });
            }

            window.onload = init;
        </script>
    </body>
</html>

ad 1. yes, trezord is a primary transport layer, webusb is only a fallback (working only for certain configuarions)

well i switched to a new vm and added back the event handling for ui-receive_device and it is working now! i really appreciate all your help, my man. thank you for all the clarification and insight.

great!
yet problem with trezord remains?

you know, i cant test right at the moment but i will a little later today -- what i did discover is that the webusb works great on a second vm but still has problems on the original vm. that leads me to believe that perhaps my trezord problems are (as you suggested) related to that original virtual machine. i'll confirm later today and let you know! thanks again.

the problem, as it turns out, has nothing to do with trezord. it is a problem with vmware + usb devices and linux. not sure the resolution but there are lots of intermittent errors in my virtual machines dmesg logs. switched to windows subsystem for linux and things are working perfectly now! thank you for all your attention on this.