move TrezorKeyring to its own package

feature/default_network_editable
Bruno 7 years ago
parent d4201ae1cc
commit 8763ea898e
  1. 1138
      app/scripts/lib/trezor-connect.js
  2. 239
      app/scripts/lib/trezorKeyring.js
  3. 5
      app/scripts/metamask-controller.js
  4. 46
      package-lock.json
  5. 2
      package.json
  6. 6
      ui/app/components/pages/create-account/connect-hardware.js

File diff suppressed because it is too large Load Diff

@ -1,239 +0,0 @@
const { EventEmitter } = require('events')
const ethUtil = require('ethereumjs-util')
const sigUtil = require('eth-sig-util')
const hdPathString = `m/44'/60'/0'/0`
const keyringType = 'Trezor Hardware'
const Transaction = require('ethereumjs-tx')
const pathBase = 'm'
const TrezorConnect = require('./trezor-connect.js')
const HDKey = require('hdkey')
const TREZOR_MIN_FIRMWARE_VERSION = '1.5.2'
const log = require('loglevel')
class TrezorKeyring extends EventEmitter {
constructor (opts = {}) {
super()
this.type = keyringType
this.accounts = []
this.hdk = new HDKey()
this.deserialize(opts)
this.page = 0
this.perPage = 5
this.unlockedAccount = 0
}
serialize () {
return Promise.resolve({
hdPath: this.hdPath,
accounts: this.accounts,
page: this.page,
})
}
deserialize (opts = {}) {
this.hdPath = opts.hdPath || hdPathString
this.accounts = opts.accounts || []
this.page = opts.page || 0
return Promise.resolve()
}
unlock () {
if (this.hdk.publicKey) return Promise.resolve()
return new Promise((resolve, reject) => {
TrezorConnect.getXPubKey(
this.hdPath,
response => {
if (response.success) {
this.hdk.publicKey = new Buffer(response.publicKey, 'hex')
this.hdk.chainCode = new Buffer(response.chainCode, 'hex')
resolve()
} else {
reject(response.error || 'Unknown error')
}
},
TREZOR_MIN_FIRMWARE_VERSION
)
})
}
setAccountToUnlock (index) {
this.unlockedAccount = parseInt(index, 10)
}
addAccounts (n = 1) {
return new Promise((resolve, reject) => {
return this.unlock()
.then(_ => {
const from = this.unlockedAccount
const to = from + 1
this.accounts = []
for (let i = from; i < to; i++) {
this.accounts.push(this._addressFromId(pathBase, i))
this.page = 0
}
resolve(this.accounts)
})
.catch(e => {
reject(e)
})
})
}
getPage () {
return new Promise((resolve, reject) => {
return this.unlock()
.then(_ => {
const from = this.page === 0 ? 0 : (this.page - 1) * this.perPage
const to = from + this.perPage
const accounts = []
for (let i = from; i < to; i++) {
accounts.push({
address: this._addressFromId(pathBase, i),
balance: 0,
index: i,
})
}
log.debug(accounts)
resolve(accounts)
})
.catch(e => {
reject(e)
})
})
}
async getPrevAccountSet () {
this.page--
return await this.getPage()
}
async getNextAccountSet () {
this.page++
return await this.getPage()
}
getAccounts () {
return Promise.resolve(this.accounts.slice())
}
// tx is an instance of the ethereumjs-transaction class.
async signTransaction (address, tx) {
return new Promise((resolve, reject) => {
log.debug('sign transaction ', address, tx)
TrezorConnect.ethereumSignTx(
this._getUnlockedAccount(),
this._normalize(tx.nonce),
this._normalize(tx.gasPrice),
this._normalize(tx.gasLimit),
this._normalize(tx.to),
this._normalize(tx.value),
this._normalize(tx.data),
tx._chainId,
response => {
if (response.success) {
tx.v = `0x${response.v.toString(16)}`
tx.r = `0x${response.r}`
tx.s = `0x${response.s}`
log.debug('about to create new tx with data', tx)
const signedTx = new Transaction(tx)
const addressSignedWith = ethUtil.toChecksumAddress(`0x${signedTx.from.toString('hex')}`)
const correctAddress = ethUtil.toChecksumAddress(address)
if (addressSignedWith !== correctAddress) {
log.error('signature doesnt match the right address', addressSignedWith, correctAddress)
throw new Error('signature doesnt match the right address')
}
resolve(signedTx)
} else {
throw new Error(response.error || 'Unknown error')
}
},
TREZOR_MIN_FIRMWARE_VERSION)
})
}
async signMessage (withAccount, data) {
throw new Error('Not supported on this device')
}
// For personal_sign, we need to prefix the message:
async signPersonalMessage (withAccount, message) {
TrezorConnect.ethereumSignMessage(this._getUnlockedAccount(), message, response => {
if (response.success) {
const signature = this._personalToRawSig(response.signature)
const addressSignedWith = sigUtil.recoverPersonalSignature({data: message, sig: signature})
const correctAddress = ethUtil.toChecksumAddress(withAccount)
if (addressSignedWith !== correctAddress) {
log.error('signature doesnt match the right address', addressSignedWith, correctAddress)
throw new Error('signature doesnt match the right address')
}
return signature
} else {
throw new Error(response.error || 'Unknown error')
}
}, TREZOR_MIN_FIRMWARE_VERSION)
}
async signTypedData (withAccount, typedData) {
// Waiting on trezor to enable this
throw new Error('Not supported on this device')
}
async exportAccount (address) {
throw new Error('Not supported on this device')
}
_padLeftEven (hex) {
return hex.length % 2 !== 0 ? `0${hex}` : hex
}
_normalize (buf) {
return this._padLeftEven(ethUtil.bufferToHex(buf).substring(2).toLowerCase())
}
_addressFromId (pathBase, i) {
const dkey = this.hdk.derive(`${pathBase}/${i}`)
const address = ethUtil
.publicToAddress(dkey.publicKey, true)
.toString('hex')
return ethUtil.toChecksumAddress(address)
}
_getUnlockedAccount () {
return `${this.hdPath}/${this.unlockedAccount}`
}
_personalToRawSig (signature) {
var v = signature['v'] - 27
v = v.toString(16)
if (v.length < 2) {
v = '0' + v
}
return '0x' + signature['r'] + signature['s'] + v
}
}
TrezorKeyring.type = keyringType
module.exports = TrezorKeyring

@ -48,7 +48,7 @@ const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
const cleanErrorStack = require('./lib/cleanErrorStack') const cleanErrorStack = require('./lib/cleanErrorStack')
const DiagnosticsReporter = require('./lib/diagnostics-reporter') const DiagnosticsReporter = require('./lib/diagnostics-reporter')
const log = require('loglevel') const log = require('loglevel')
const TrezorKeyring = require('./lib/trezorKeyring') const TrezorKeyring = require('eth-trezor-keyring')
module.exports = class MetamaskController extends EventEmitter { module.exports = class MetamaskController extends EventEmitter {
@ -549,7 +549,8 @@ module.exports = class MetamaskController extends EventEmitter {
throw new Error('MetamaskController - No Trezor Hardware Keyring found') throw new Error('MetamaskController - No Trezor Hardware Keyring found')
} }
const accounts = page === -1 ? await keyring.getPrevAccountSet(this.provider) : await keyring.getNextAccountSet(this.provider) const accounts = await keyring.getPage(page)
this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) this.accountTracker.syncWithAddresses(accounts.map(a => a.address))
return accounts return accounts

46
package-lock.json generated

@ -8488,6 +8488,52 @@
} }
} }
}, },
"eth-trezor-keyring": {
"version": "github:brunobar79/eth-trezor-keyring#c138d26c36a01f15be5e12a81e4fcbf56b044fa4",
"from": "github:brunobar79/eth-trezor-keyring#c138d26c36a01f15be5e12a81e4fcbf56b044fa4",
"requires": {
"eth-sig-util": "^1.4.2",
"ethereumjs-tx": "^1.3.4",
"ethereumjs-util": "^5.1.5",
"events": "^2.0.0",
"hdkey": "0.8.0"
},
"dependencies": {
"ethereum-common": {
"version": "0.0.18",
"resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz",
"integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8="
},
"ethereumjs-tx": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.4.tgz",
"integrity": "sha512-kOgUd5jC+0tgV7t52UDECMMz9Uf+Lro+6fSpCvzWemtXfMEcwI3EOxf5mVPMRbTFkMMhuERokNNVF3jItAjidg==",
"requires": {
"ethereum-common": "^0.0.18",
"ethereumjs-util": "^5.0.0"
}
},
"ethereumjs-util": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
"requires": {
"bn.js": "^4.11.0",
"create-hash": "^1.1.2",
"ethjs-util": "^0.1.3",
"keccak": "^1.0.2",
"rlp": "^2.0.0",
"safe-buffer": "^5.1.1",
"secp256k1": "^3.0.1"
}
},
"events": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
"integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg=="
}
}
},
"eth-tx-summary": { "eth-tx-summary": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/eth-tx-summary/-/eth-tx-summary-3.2.1.tgz", "resolved": "https://registry.npmjs.org/eth-tx-summary/-/eth-tx-summary-3.2.1.tgz",

@ -102,6 +102,7 @@
"eth-query": "^2.1.2", "eth-query": "^2.1.2",
"eth-sig-util": "^1.4.2", "eth-sig-util": "^1.4.2",
"eth-token-tracker": "^1.1.4", "eth-token-tracker": "^1.1.4",
"eth-trezor-keyring": "github:brunobar79/eth-trezor-keyring#c138d26c36a01f15be5e12a81e4fcbf56b044fa4",
"ethereumjs-abi": "^0.6.4", "ethereumjs-abi": "^0.6.4",
"ethereumjs-tx": "^1.3.0", "ethereumjs-tx": "^1.3.0",
"ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9", "ethereumjs-util": "github:ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
@ -124,7 +125,6 @@
"gulp-eslint": "^4.0.0", "gulp-eslint": "^4.0.0",
"gulp-sass": "^4.0.0", "gulp-sass": "^4.0.0",
"hat": "0.0.3", "hat": "0.0.3",
"hdkey": "0.8.0",
"human-standard-token-abi": "^1.0.2", "human-standard-token-abi": "^1.0.2",
"idb-global": "^2.1.0", "idb-global": "^2.1.0",
"identicon.js": "^2.3.1", "identicon.js": "^2.3.1",

@ -25,10 +25,10 @@ class ConnectHardwareForm extends Component {
return null return null
} }
this.setState({ btnText: 'Connecting...' }) this.setState({ btnText: 'Connecting...' })
this.getPage() this.getPage(1)
} }
getPage (page = 1) { getPage (page) {
this.props this.props
.connectHardware('trezor', page) .connectHardware('trezor', page)
.then(accounts => { .then(accounts => {
@ -133,7 +133,7 @@ class ConnectHardwareForm extends Component {
h( h(
'button.btn-primary.hw-list-pagination__button', 'button.btn-primary.hw-list-pagination__button',
{ {
onClick: () => this.getPage(), onClick: () => this.getPage(1),
}, },
'Next >' 'Next >'
), ),

Loading…
Cancel
Save