8000 Added Bluetooth Ledger support by MonikaCat · Pull Request #510 · forbole/big-dipper · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Added Bluetooth Ledger support #510

Merged
merged 12 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [UNRELEASED]
* [#387] Added Bluetooth Ledger support

## v0.41.x-11
* Replaced Random Validators and Chart components with Latest Blocks and Latest Transactions components on homepage
* Update meta data to align with setting values
Expand Down
3 changes: 2 additions & 1 deletion both/i18n/en-us.i18n.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,10 @@ accounts:
signInText: 'You are signed in as '
toLoginAs: 'To log in as'
signInWithLedger: 'Sign In With Ledger'
signInWarning: 'Please make sure your Ledger device is connected and <strong class="text-primary">{$network} App {$version} or above</strong> is opened.'
signInWarning: 'Please make sure your Ledger device is turned on and <strong class="text-primary">{$network} App {$version} or above</strong> is opened.'
pleaseAccept: 'please accept in your Ledger device.'
noRewards: 'No Rewards'
BLESupport: 'Bluetooth connection is currently only supported on Google Chrome Browser.'
activities:
single: 'A'
happened: 'happened.'
Expand Down
1 change: 1 addition & 0 deletions client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { render } from 'react-dom';

CURRENTUSERADDR = 'ledgerUserAddress';
CURRENTUSERPUBKEY = 'ledgerUserPubKey';
BLELEDGERCONNECTION = 'ledgerBLEConnection'

// import { onPageLoad } from 'meteor/server-render';

Expand Down
2 changes: 1 addition & 1 deletion imports/api/ledger/server/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Meteor.methods({
"chain_id": Meteor.settings.public.chainId,
"gas_adjustment": adjustment,
"account_number": accountNumber,
"sequence": sequence,
"sequence": sequence.toString(),
"simulate": true
}
};
Expand Down
8 changes: 5 additions & 3 deletions imports/ui/ledger/LedgerActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class LedgerButton extends Component {
errorMessage: '',
user: localStorage.getItem(CURRENTUSERADDR),
pubKey: localStorage.getItem(CURRENTUSERPUBKEY),
transportBLE: localStorage.getItem(BLELEDGERCONNECTION),
memo: DEFAULT_MEMO
};
this.ledger = new Ledger({testModeAllowed: false});
Expand Down Expand Up @@ -194,7 +195,8 @@ class LedgerButton extends Component {
if (state.user !== localStorage.getItem(CURRENTUSERADDR)) {
return {
user: localStorage.getItem(CURRENTUSERADDR),
pubKey: localStorage.getItem(CURRENTUSERPUBKEY)
pubKey: localStorage.getItem(CURRENTUSERPUBKEY),
transportBLE: localStorage.getItem(BLELEDGERCONNECTION)
};
}
return null;
Expand Down Expand Up @@ -306,7 +308,7 @@ class LedgerButton extends Component {
}

tryConnect = () => {
this.ledger.getCosmosAddress().then((res) => {
this.ledger.getCosmosAddress(this.state.transportBLE).then((res) => {
if (res.address == this.state.user)
this.setState({
success: true,
Expand Down Expand Up @@ -437,7 +439,7 @@ class LedgerButton extends Component {
let txMsg = this.state.txMsg;
const txContext = this.getTxContext();
const bytesToSign = Ledger.getBytesToSign(txMsg, txContext);
this.ledger.sign(bytesToSign).then((sig) => {
this.ledger.sign(bytesToSign, this.state.transportBLE).then((sig) => {
try {
Ledger.applySignature(txMsg, txContext, sig);
Meteor.call('transaction.submit', txMsg, (err, res) => {
Expand Down
37 changes: 30 additions & 7 deletions imports/ui/ledger/LedgerModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ class LedgerModal extends React.Component {
super(props);
this.state = {
loading: false,
activeTab: '1'
activeTab: '1',
transportBLE: localStorage.getItem(BLELEDGERCONNECTION) ?? false
};
this.ledger = new Ledger({testModeAllowed: false});
}

autoOpenModal = () => {
if (!this.props.isOpen && this.props.handleLoginConfirmed) {
this.tryConnect(5000);
// this.tryConnect(5000);
this.props.toggle(true);
}
}
Expand All @@ -28,15 +29,30 @@ class LedgerModal extends React.Component {

componentDidUpdate(prevProps, prevState) {
this.autoOpenModal();
if (this.props.isOpen && !prevProps.isOpen) {
let bleTransport = this.state.transportBLE
if (bleTransport != prevState.transportBLE) {
this.tryConnect();
}
}

connectionSelection = async (e) => {
e.persist();
if(e?.currentTarget?.value === "usb"){
await this.setState({ transportBLE: false })
this.tryConnect()

}
if (e?.currentTarget?.value === "bluetooth") {
await this.setState({ transportBLE: true })
this.tryConnect()

}
}

tryConnect = (timeout=undefined) => {
if (this.state.loading) return
this.setState({ loading: true, errorMessage: '' })
this.ledger.getCosmosAddress(timeout).then((res) => {
this.ledger.getCosmosAddress(this.state.transportBLE).then((res) => {
let currentUser = localStorage.getItem(CURRENTUSERADDR);
if (this.props.handleLoginConfirmed && res.address === currentUser) {
this.closeModal(true)
Expand All @@ -59,11 +75,15 @@ class LedgerModal extends React.Component {
});
}




trySignIn = () => {
this.setState({ loading: true, errorMessage: '' })
this.ledger.confirmLedgerAddress().then((res) => {
this.ledger.confirmLedgerAddress(this.state.transportBLE).then((res) => {
localStorage.setItem(CURRENTUSERADDR, this.state.address);
localStorage.setItem(CURRENTUSERPUBKEY, this.state.pubKey);
localStorage.setItem(BLELEDGERCONNECTION, this.state.transportBLE);
this.props.refreshApp();
this.closeModal(true);
}, (err) => {
Expand All @@ -74,8 +94,6 @@ class LedgerModal extends React.Component {
}

getActionButton() {
if (this.state.activeTab === '1' && !this.state.loading)
return <Button color="primary" >
if (this.state.activeTab === '2' && this.state.errorMessage !== '')
return <Button color="primary" >
}
Expand All @@ -102,6 +120,11 @@ class LedgerModal extends React.Component {
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
<T _purify={false} network={Meteor.settings.public.ledger.appName} version={Meteor.settings.public.ledger.appVersion}>accounts.signInWarning</T>
<div className="d-flex justify-content-center">
<Button color="secondary" value="usb" className="mt-3 mr-4"><span><img src="/img/usb.svg" alt="USB" style={{height: "25px"}}/><T>USB</T></span></Button>
<Button color="secondary" value="bluetooth" className="mt-3 "><span><img src="/img/bluetooth.svg" alt="Bluetooth" style={{ height: "25px" }} /><T>Bluetooth</T></span></Button>
</div>
<h6 className="error-message text-center mt-3"><T>accounts.BLESupport</T></h6>
</TabPane>
<TabPane tabId="2">
{this.state.currentUser?<span>You are currently logged in as <strong className="text-primary d-block">{this.state.currentUser}.</strong></span>:null}
Expand Down
66 changes: 46 additions & 20 deletions imports/ui/ledger/ledger.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// https://github.com/cosmos/ledger-cosmos-js/blob/master/src/index.js
import 'babel-polyfill';
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
import BluetoothTransport from "@ledgerhq/hw-transport-web-ble";
import CosmosApp from "ledger-cosmos-js"
import { signatureImport } from "secp256k1"
import semver from "semver"
Expand Down Expand Up @@ -54,7 +55,7 @@ export class Ledger {
async testDevice() {
// poll device with low timeout to check if the device is connected
const secondsTimeout = 3 // a lower value always timeouts
await this.connect(secondsTimeout)
await this.connect(secondsTimeout, false)
}
async isSendingData() {
// check if the device is connected or on screensaver mode
Expand All @@ -63,33 +64,56 @@ export class Ledger {
timeoutMessag: "Could not find a connected and unlocked Ledger device"
})
}
async isReady() {
async isReady(transportBLE) {
// check if the version is supported
const version = await this.getCosmosAppVersion()
const version = await this.getCosmosAppVersion(transportBLE)

if (!semver.gte(version, REQUIRED_COSMOS_APP_VERSION)) {
const msg = `Outdated version: Please update Ledger Cosmos App to the latest version.`
throw new Error(msg)
}

// throws if not open
await this.isCosmosAppOpen()
await this.isCosmosAppOpen(transportBLE)
}
// connects to the device and checks for compatibility
async connect(timeout = INTERACTION_TIMEOUT) {
async connect(timeout = INTERACTION_TIMEOUT, transportBLE) {
// assume well connection if connected once
if (this.cosmosApp) return

const transport = await TransportWebUSB.create(timeout)
let transport;
if(transportBLE === true || transportBLE === 'true'){
transport = await BluetoothTransport.create(timeout)
}
else{
transport= await TransportWebUSB.create(timeout)
}
const cosmosLedgerApp = new CosmosApp(transport)

this.cosmosApp = cosmosLedgerApp

await this.isSendingData()
await this.isReady()
await this.isReady(transportBLE)
}

async getDevice(){
return new Promise((resolve, reject) => {
const subscription = BluetoothTransport.listen({
next(event) {
if (event.type === 'add') {
subscription.unsubscribe();
resolve(event.descriptor);
}
},
error(error) {
reject(error);
},
complete() {
}
});
});
}
async getCosmosAppVersion() {
await this.connect()
async getCosmosAppVersion(transportBLE) {
await this.connect(INTERACTION_TIMEOUT, transportBLE)

const response = await this.cosmosApp.getVersion()
this.checkLedgerErrors(response)
Expand All @@ -99,8 +123,8 @@ export class Ledger {

return version
}
async isCosmosAppOpen() {
await this.connect()
async isCosmosAppOpen(transportBLE) {
await this.connect(INTERACTION_TIMEOUT, transportBLE)

const response = await this.cosmosApp.appInfo()
this.checkLedgerErrors(response)
Expand All @@ -110,21 +134,21 @@ export class Ledger {
throw new Error(`Close ${appName} and open the ${Meteor.settings.public.ledger.appName} app`)
}
}
async getPubKey() {
await this.connect()
async getPubKey(transportBLE) {
await this.connect(INTERACTION_TIMEOUT, transportBLE)

const response = await this.cosmosApp.publicKey(HDPATH)
this.checkLedgerErrors(response)
return response.compressed_pk
}
async getCosmosAddress() {
await this.connect()
async getCosmosAddress(transportBLE) {
await this.connect(INTERACTION_TIMEOUT, transportBLE)

const pubKey = await this.getPubKey(this.cosmosApp)
return {pubKey, address:createCosmosAddress(pubKey)}
}
async confirmLedgerAddress() {
await this.connect()
async confirmLedgerAddress(transportBLE) {
await this.connect(INTERACTION_TIMEOUT, transportBLE)
const cosmosAppVersion = await this.getCosmosAppVersion()

if (semver.lt(cosmosAppVersion, REQUIRED_COSMOS_APP_VERSION)) {
Expand All @@ -141,8 +165,8 @@ export class Ledger {
})
}

async sign(signMessage) {
await this.connect()
async sign(signMessage, transportBLE) {
await this.connect(INTERACTION_TIMEOUT, transportBLE)

const response = await this.cosmosApp.sign(HDPATH, signMessage)
this.checkLedgerErrors(response)
Expand Down Expand Up @@ -183,6 +207,8 @@ export class Ledger {
`Your ${Meteor.settings.public.ledger.appName} Ledger App is not up to date. ` +
`Please update to version ${REQUIRED_COSMOS_APP_VERSION}.`
)
case `Web Bluetooth API globally disabled`:
throw new Error(`Bluetooth not supported. Please use the latest version of Chrome browser.`)
case `No errors`:
// do nothing
break
Expand Down
66 changes: 66 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@babel/runtime": "^7.13.17",
"@ledgerhq/hw-transport-web-ble": "^5.50.0",
"@ledgerhq/hw-transport-webusb": "^5.49.0",
"@types/meteor-universe-i18n": "^1.14.5",
"babel-polyfill": "^6.26.0",
Expand Down
Loading
0