|
|
|
const EventEmitter = require('events').EventEmitter
|
|
|
|
const hdkey = require('ethereumjs-wallet/hdkey')
|
|
|
|
const bip39 = require('bip39')
|
|
|
|
const ethUtil = require('ethereumjs-util')
|
|
|
|
|
|
|
|
// *Internal Deps
|
|
|
|
const sigUtil = require('../lib/sig-util')
|
|
|
|
|
|
|
|
// Options:
|
|
|
|
const hdPathString = `m/44'/60'/0'/0`
|
|
|
|
const type = 'HD Key Tree'
|
|
|
|
|
|
|
|
class HdKeyring extends EventEmitter {
|
|
|
|
|
|
|
|
/* PUBLIC METHODS */
|
|
|
|
|
|
|
|
constructor (opts = {}) {
|
|
|
|
super()
|
|
|
|
this.type = type
|
|
|
|
this.deserialize(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
serialize () {
|
|
|
|
return Promise.resolve({
|
|
|
|
mnemonic: this.mnemonic,
|
|
|
|
numberOfAccounts: this.wallets.length,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
deserialize (opts = {}) {
|
|
|
|
this.opts = opts || {}
|
|
|
|
this.wallets = []
|
|
|
|
this.mnemonic = null
|
|
|
|
this.root = null
|
|
|
|
|
|
|
|
if (opts.mnemonic) {
|
|
|
|
this._initFromMnemonic(opts.mnemonic)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.numberOfAccounts) {
|
|
|
|
return this.addAccounts(opts.numberOfAccounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve([])
|
|
|
|
}
|
|
|
|
|
|
|
|
addAccounts (numberOfAccounts = 1) {
|
|
|
|
if (!this.root) {
|
|
|
|
this._initFromMnemonic(bip39.generateMnemonic())
|
|
|
|
}
|
|
|
|
|
|
|
|
const oldLen = this.wallets.length
|
|
|
|
const newWallets = []
|
|
|
|
for (let i = oldLen; i < numberOfAccounts + oldLen; i++) {
|
|
|
|
const child = this.root.deriveChild(i)
|
|
|
|
const wallet = child.getWallet()
|
|
|
|
newWallets.push(wallet)
|
|
|
|
this.wallets.push(wallet)
|
|
|
|
}
|
|
|
|
const hexWallets = newWallets.map(w => w.getAddress().toString('hex'))
|
|
|
|
return Promise.resolve(hexWallets)
|
|
|
|
}
|
|
|
|
|
|
|
|
getAccounts () {
|
|
|
|
return Promise.resolve(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 Promise.resolve(tx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// For eth_sign, we need to sign transactions:
|
|
|
|
signMessage (withAccount, msgHex) {
|
|
|
|
const wallet = this._getWalletForAccount(withAccount)
|
|
|
|
const privKey = wallet.getPrivateKey()
|
|
|
|
const msgBuffer = ethUtil.toBuffer(msgHex)
|
|
|
|
const msgHash = ethUtil.hashPersonalMessage(msgBuffer)
|
|
|
|
const msgSig = ethUtil.ecsign(msgHash, privKey)
|
|
|
|
const rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
|
|
|
|
return Promise.resolve(rawMsgSig)
|
|
|
|
}
|
|
|
|
|
|
|
|
exportAccount (address) {
|
|
|
|
const wallet = this._getWalletForAccount(address)
|
|
|
|
return Promise.resolve(wallet.getPrivateKey().toString('hex'))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PRIVATE METHODS */
|
|
|
|
|
|
|
|
_initFromMnemonic (mnemonic) {
|
|
|
|
this.mnemonic = mnemonic
|
|
|
|
const seed = bip39.mnemonicToSeed(mnemonic)
|
|
|
|
this.hdWallet = hdkey.fromMasterSeed(seed)
|
|
|
|
this.root = this.hdWallet.derivePath(hdPathString)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_getWalletForAccount (account) {
|
|
|
|
return this.wallets.find((w) => {
|
|
|
|
const address = w.getAddress().toString('hex')
|
|
|
|
return ((address === account) || (sigUtil.normalize(address) === account))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HdKeyring.type = type
|
|
|
|
module.exports = HdKeyring
|