2016-10-28 01:50:01 +02:00
|
|
|
const EventEmitter = require('events').EventEmitter
|
|
|
|
const hdkey = require('ethereumjs-wallet/hdkey')
|
|
|
|
const bip39 = require('bip39')
|
|
|
|
const ethUtil = require('ethereumjs-util')
|
|
|
|
const type = 'HD Key Tree'
|
|
|
|
const sigUtil = require('../lib/sig-util')
|
|
|
|
|
2016-10-28 02:23:26 +02:00
|
|
|
const hdPathString = `m/44'/60'/0'/0`
|
|
|
|
|
|
|
|
module.exports = class HdKeyring extends EventEmitter {
|
2016-10-28 01:50:01 +02:00
|
|
|
|
|
|
|
static type() {
|
|
|
|
return type
|
|
|
|
}
|
|
|
|
|
2016-11-02 01:00:17 +01:00
|
|
|
constructor(opts = {}) {
|
2016-10-28 01:50:01 +02:00
|
|
|
super()
|
|
|
|
this.type = type
|
2016-11-01 19:25:38 +01:00
|
|
|
this.deserialize(opts)
|
|
|
|
}
|
|
|
|
|
2016-11-02 23:04:50 +01:00
|
|
|
deserialize(opts = {}) {
|
|
|
|
this.opts = opts || {}
|
2016-10-28 01:50:01 +02:00
|
|
|
this.wallets = []
|
|
|
|
this.mnemonic = null
|
2016-11-01 19:25:38 +01:00
|
|
|
this.root = null
|
|
|
|
|
|
|
|
if ('mnemonic' in opts) {
|
|
|
|
this.initFromMnemonic(opts.mnemonic)
|
|
|
|
}
|
|
|
|
|
|
|
|
if ('n' in opts) {
|
|
|
|
this.addAccounts(opts.n)
|
|
|
|
}
|
2016-10-28 01:50:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
initFromMnemonic(mnemonic) {
|
|
|
|
this.mnemonic = mnemonic
|
2016-11-02 23:04:50 +01:00
|
|
|
const seed = bip39.mnemonicToSeed(mnemonic)
|
2016-10-28 01:50:01 +02:00
|
|
|
this.hdWallet = hdkey.fromMasterSeed(seed)
|
2016-10-28 02:23:26 +02:00
|
|
|
this.root = this.hdWallet.derivePath(hdPathString)
|
2016-10-28 01:50:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
serialize() {
|
|
|
|
return {
|
|
|
|
mnemonic: this.mnemonic,
|
|
|
|
n: this.wallets.length,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:40:23 +01:00
|
|
|
exportAccount(address) {
|
|
|
|
const wallet = this.getWalletForAccount(address)
|
|
|
|
return wallet.getPrivateKey().toString('hex')
|
|
|
|
}
|
|
|
|
|
2016-10-28 01:50:01 +02:00
|
|
|
addAccounts(n = 1) {
|
2016-10-28 02:23:26 +02:00
|
|
|
if (!this.root) {
|
|
|
|
this.initFromMnemonic(bip39.generateMnemonic())
|
|
|
|
}
|
|
|
|
|
|
|
|
const oldLen = this.wallets.length
|
2016-10-28 01:50:01 +02:00
|
|
|
const newWallets = []
|
2016-10-28 02:23:26 +02:00
|
|
|
for (let i = oldLen; i < n + oldLen; i++) {
|
|
|
|
const child = this.root.deriveChild(i)
|
|
|
|
const wallet = child.getWallet()
|
2016-10-28 01:50:01 +02:00
|
|
|
newWallets.push(wallet)
|
|
|
|
this.wallets.push(wallet)
|
|
|
|
}
|
|
|
|
return newWallets.map(w => w.getAddress().toString('hex'))
|
|
|
|
}
|
|
|
|
|
|
|
|
getAccounts() {
|
|
|
|
return this.wallets.map(w => w.getAddress().toString('hex'))
|
|
|
|
}
|
|
|
|
|
|
|
|
// tx is an instance of the ethereumjs-transaction class.
|
|
|
|
signTransaction(address, tx) {
|
|
|
|
const wallet = this.getWalletForAccount(address)
|
|
|
|
var privKey = wallet.getPrivateKey()
|
|
|
|
tx.sign(privKey)
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
|
|
|
|
// For eth_sign, we need to sign transactions:
|
|
|
|
signMessage(withAccount, data) {
|
|
|
|
const wallet = this.getWalletForAccount(withAccount)
|
|
|
|
const message = ethUtil.removeHexPrefix(data)
|
|
|
|
var privKey = wallet.getPrivateKey()
|
|
|
|
var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
|
|
|
|
var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
|
|
|
return rawMsgSig
|
|
|
|
}
|
|
|
|
|
|
|
|
getWalletForAccount(account) {
|
2016-11-03 23:40:23 +01:00
|
|
|
return this.wallets.find((w) => {
|
|
|
|
const address = w.getAddress().toString('hex')
|
|
|
|
return ((address === account) || (normalize(address) === account))
|
|
|
|
})
|
2016-10-28 01:50:01 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:40:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function normalize(address) {
|
|
|
|
return ethUtil.addHexPrefix(address.toLowerCase())
|
2016-10-28 01:50:01 +02:00
|
|
|
}
|