Skip to content
This repository has been archived by the owner on Jun 27, 2022. It is now read-only.

TransportNodeHid.create() on MAC is not working #213

Open
hirishh opened this issue Sep 11, 2018 · 14 comments
Open

TransportNodeHid.create() on MAC is not working #213

hirishh opened this issue Sep 11, 2018 · 14 comments

Comments

@hirishh
Copy link

hirishh commented Sep 11, 2018

Hello,

I have an application that is using TransportNodeHid.create() in order to connect and use the Ledger Nano S. What I see is that on Linux and Windows is working fine, but on MAC is not able to connect to the ledger.

I've debugged a lot and the issue is here: https://github.com/LedgerHQ/ledgerjs/blob/master/packages/hw-transport-node-hid/src/getDevices.js#L7

On MAC High Sierra, HID.devices() is returning correctly the information of my Ledger Nano S, but is filtered out because of interface === 0. Infact, I have interface === -1.

Proof here:
image

Best,

hirishh

@gre
Copy link
Contributor

gre commented Sep 12, 2018

actually on Mac, the logic does not even use interface, it uses usagePage

const filterInterface = device =>
  ["win32", "darwin"].includes(process.platform)
    ? device.usagePage === 0xffa0
    : device.interface === 0;

on Windows and Mac (darwin), it will filter device.usagePage === 0xffa0 , and on Linux device.interface === 0. In the case you screenshot it seems to be correct, usagePage is 0xffa0 (65440).

could you make sure that process.platform is returning "darwin" for you?

@hirishh
Copy link
Author

hirishh commented Sep 12, 2018

During my debugging, console.info(process.platform) showed undefined

@hirishh
Copy link
Author

hirishh commented Sep 12, 2018

After more debugging I can say that all 3 platforms return undefined for process.platform when called from renderer process, but windows and linux have device.interface = 0 (-1 on mac)

@gre
Copy link
Contributor

gre commented Sep 13, 2018

ok make sense for renderer process.
We'll have to see if in case of windows, it's ok to move to use interface as filter (instead of current usagePage).
I wish we can do like in the code snippet below in future (that would remove the need to check the platform – it's always better to feature detect rather than looking at the OS), but we'll need to test that it detects the device in all cases (at least dashboard & btc app) and that there is no false positive (typically that we don't return more than one "device" for a single device usb connection (because a device have potentially 3 interfaces now: hid, u2f, webusb)

const filterInterface = device =>
// filter using interface if available, otherwise fallback on usagePage filtering.
  device.interface === -1
    ? device.usagePage === 0xffa0
    : device.interface === 0;

@tylerlevine
Copy link

Is there any workaround for this issue? This is a blocker for including ledger support in our electron-based app. How does ledger live handle this issue?

@hirishh
Copy link
Author

hirishh commented Jan 15, 2019

hello @tylerlevine,
this error happens on electron when you try to create a transport from the UI process. You can use IPC to communicate between main process <-> rendering process and make all the usb action on the main process. This is my code: https://github.com/hirishh/liskish-wallet/blob/2.0.0/app/src/hw/hwManager.js#L57

It's quite similar to the code on ledger-live

Best,

@gre
Copy link
Contributor

gre commented Jan 15, 2019

Run the device code NOT in the renderer thread and you should be fine, i think the present problem happen only in renderer because process.platform is undefined there. not the case in main thread or in a node forked thread.

We recommand to use a dedicated thread to run the transport because node-hid code is synchronous and block the thread. You don't want to block the main thread or the renderer thread.
Ledger Live uses a third "internal" thread.

@gre
Copy link
Contributor

gre commented Jan 15, 2019

@hirishh was too fast 🚀

@itsMikeLowrey
Copy link

itsMikeLowrey commented Feb 9, 2019

Hi I am using mac also and ran into some issues in electron with TransportNodeHid.create() so I moved to fork a child process. @hirishh has a great example of how to use this code on the main process. I am unsure how to run it on a forked child process , since it can not be ran in any render process. I keep running into the issue that forked process does support babel\polyfill. I just wanted to confirm that I am on the right path and my understanding of the best practices is correct. Any feedback would be greatly appreciated.

@gre
Copy link
Contributor

gre commented Feb 9, 2019

@itsMikeLowrey can you try to do this instead: TransportNodeHid.open("") ? It is an alternative way that don't use listen() and will directly attempt to open() the device. It's a lighter way and as we are experiencing some issue with "node-usb" these days, it might be linked.

@itsMikeLowrey
Copy link

@gre Thank you so much for that tip. It dosnet seem to be working for me right now, but ill try to move some things around and get back to you if I figure something out.

@gre
Copy link
Contributor

gre commented Feb 9, 2019

Please keep me updated with the error you are having.

import "babel-polyfill" might be required if related to this.

@itsMikeLowrey
Copy link

itsMikeLowrey commented Feb 9, 2019

@gre Thank you so much. It works with the code below:
Calling function
setupLedgar: async function () { await getBtcAddress().then(a => console.log(a)) }
externel javascript file that features edit to example code on read.me

import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
import AppBtc from '@ledgerhq/hw-app-btc'
const getBtcAddress = async () => {
  const transport = await TransportNodeHid.open('')
  console.log('ran')
  const btc = new AppBtc(transport)
  const result = await btc.getWalletPublicKey("44'/0'/0'/0/0")
  return result.bitcoinAddress
}
export { getBtcAddress }

I am using vue.js with electron builder. I am so excited that this works now. I know that this is not the best practice since I am running this in my render process, but I am so glad that it is currently working. I did not need import "babel-polyfill" or anything else. Is there any risk ,beside freezing the app, to running the code on the render process with the gui? thank you again for all the help.

@gre
Copy link
Contributor

gre commented Dec 17, 2019

babel-polyfill is no longer required in v5.0.0.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants