2016-02-11 02:44:46 +01:00
|
|
|
const EventEmitter = require('events').EventEmitter
|
|
|
|
const inherits = require('util').inherits
|
2016-07-14 08:39:44 +02:00
|
|
|
const async = require('async')
|
2016-06-17 04:51:34 +02:00
|
|
|
const ethUtil = require('ethereumjs-util')
|
2016-07-14 08:39:44 +02:00
|
|
|
const EthQuery = require('eth-query')
|
2016-03-03 07:58:23 +01:00
|
|
|
const LightwalletKeyStore = require('eth-lightwallet').keystore
|
2016-02-11 02:44:46 +01:00
|
|
|
const clone = require('clone')
|
|
|
|
const extend = require('xtend')
|
|
|
|
const createId = require('web3-provider-engine/util/random-id')
|
2016-06-17 04:51:34 +02:00
|
|
|
const ethBinToOps = require('eth-bin-to-ops')
|
2016-03-29 21:21:46 +02:00
|
|
|
const autoFaucet = require('./auto-faucet')
|
2016-05-03 23:32:22 +02:00
|
|
|
const messageManager = require('./message-manager')
|
2016-04-25 23:14:34 +02:00
|
|
|
const DEFAULT_RPC = 'https://testrpc.metamask.io/'
|
2016-06-15 23:58:06 +02:00
|
|
|
const IdManagement = require('./id-management')
|
2016-02-11 02:44:46 +01:00
|
|
|
|
|
|
|
module.exports = IdentityStore
|
|
|
|
|
|
|
|
inherits(IdentityStore, EventEmitter)
|
2016-06-21 22:18:32 +02:00
|
|
|
function IdentityStore (opts = {}) {
|
2016-03-25 20:41:18 +01:00
|
|
|
EventEmitter.call(this)
|
2016-02-11 02:44:46 +01:00
|
|
|
|
|
|
|
// we just use the ethStore to auto-add accounts
|
2016-04-28 03:04:33 +02:00
|
|
|
this._ethStore = opts.ethStore
|
2016-06-25 00:52:56 +02:00
|
|
|
this.configManager = opts.configManager
|
2016-03-03 07:58:23 +01:00
|
|
|
// lightwallet key store
|
2016-03-25 20:41:18 +01:00
|
|
|
this._keyStore = null
|
2016-03-03 07:58:23 +01:00
|
|
|
// lightwallet wrapper
|
2016-03-25 20:41:18 +01:00
|
|
|
this._idmgmt = null
|
|
|
|
|
|
|
|
this.hdPathString = "m/44'/60'/0'/0"
|
2016-02-11 02:44:46 +01:00
|
|
|
|
2016-03-25 20:41:18 +01:00
|
|
|
this._currentState = {
|
2016-02-11 02:44:46 +01:00
|
|
|
selectedAddress: null,
|
|
|
|
identities: {},
|
|
|
|
}
|
2016-05-03 23:32:22 +02:00
|
|
|
|
2016-02-11 02:44:46 +01:00
|
|
|
// not part of serilized metamask state - only kept in memory
|
2016-03-25 20:41:18 +01:00
|
|
|
this._unconfTxCbs = {}
|
2016-04-28 23:16:24 +02:00
|
|
|
this._unconfMsgCbs = {}
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// public
|
|
|
|
//
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.createNewVault = function (password, entropy, cb) {
|
2016-03-24 18:32:50 +01:00
|
|
|
delete this._keyStore
|
2016-06-25 01:13:27 +02:00
|
|
|
|
2016-03-24 18:32:50 +01:00
|
|
|
this._createIdmgmt(password, null, entropy, (err) => {
|
2016-03-03 07:58:23 +01:00
|
|
|
if (err) return cb(err)
|
2016-03-31 19:47:40 +02:00
|
|
|
|
2016-03-24 18:32:50 +01:00
|
|
|
this._loadIdentities()
|
|
|
|
this._didUpdate()
|
2016-03-29 21:21:46 +02:00
|
|
|
this._autoFaucet()
|
2016-03-31 19:47:40 +02:00
|
|
|
|
2016-06-25 00:52:56 +02:00
|
|
|
this.configManager.setShowSeedWords(true)
|
2016-03-31 19:47:40 +02:00
|
|
|
var seedWords = this._idmgmt.getSeed()
|
2016-03-03 07:58:23 +01:00
|
|
|
cb(null, seedWords)
|
|
|
|
})
|
2016-02-17 09:55:57 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.recoverSeed = function (cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
this.configManager.setShowSeedWords(true)
|
2016-06-03 02:11:10 +02:00
|
|
|
if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.'))
|
2016-06-03 01:52:18 +02:00
|
|
|
var seedWords = this._idmgmt.getSeed()
|
|
|
|
cb(null, seedWords)
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.recoverFromSeed = function (password, seed, cb) {
|
2016-03-25 22:51:19 +01:00
|
|
|
this._createIdmgmt(password, seed, null, (err) => {
|
2016-03-15 21:39:12 +01:00
|
|
|
if (err) return cb(err)
|
2016-03-25 22:51:19 +01:00
|
|
|
|
|
|
|
this._loadIdentities()
|
|
|
|
this._didUpdate()
|
2016-03-28 21:35:18 +02:00
|
|
|
cb(null, this.getState())
|
2016-03-15 21:39:12 +01:00
|
|
|
})
|
|
|
|
}
|
2016-02-17 09:55:57 +01:00
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.setStore = function (store) {
|
2016-03-25 22:51:19 +01:00
|
|
|
this._ethStore = store
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.clearSeedWordCache = function (cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-31 19:47:40 +02:00
|
|
|
configManager.setShowSeedWords(false)
|
2016-04-25 23:14:34 +02:00
|
|
|
cb(null, configManager.getSelectedAccount())
|
2016-03-24 18:32:50 +01:00
|
|
|
}
|
2016-02-17 09:55:57 +01:00
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.getState = function () {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-31 19:47:40 +02:00
|
|
|
var seedWords = this.getSeedIfUnlocked()
|
2016-03-25 22:51:19 +01:00
|
|
|
return clone(extend(this._currentState, {
|
2016-03-31 19:47:40 +02:00
|
|
|
isInitialized: !!configManager.getWallet() && !seedWords,
|
2016-03-25 22:51:19 +01:00
|
|
|
isUnlocked: this._isUnlocked(),
|
2016-03-31 19:47:40 +02:00
|
|
|
seedWords: seedWords,
|
2016-06-17 01:41:33 +02:00
|
|
|
isConfirmed: configManager.getConfirmed(),
|
2016-07-21 22:41:10 +02:00
|
|
|
isEthConfirmed: configManager.getShouldntShowWarning(),
|
2016-04-19 02:19:20 +02:00
|
|
|
unconfTxs: configManager.unconfirmedTxs(),
|
2016-04-19 02:19:58 +02:00
|
|
|
transactions: configManager.getTxList(),
|
2016-05-03 23:32:22 +02:00
|
|
|
unconfMsgs: messageManager.unconfirmedMsgs(),
|
|
|
|
messages: messageManager.getMsgList(),
|
2016-04-25 23:14:34 +02:00
|
|
|
selectedAddress: configManager.getSelectedAccount(),
|
2016-08-18 19:40:35 +02:00
|
|
|
shapeShiftTxList: configManager.getShapeShiftTxList(),
|
2016-07-21 19:15:34 +02:00
|
|
|
currentFiat: configManager.getCurrentFiat(),
|
2016-07-21 23:08:26 +02:00
|
|
|
conversionRate: configManager.getConversionRate(),
|
|
|
|
conversionDate: configManager.getConversionDate(),
|
2016-02-11 02:44:46 +01:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.getSeedIfUnlocked = function () {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-31 19:47:40 +02:00
|
|
|
var showSeed = configManager.getShouldShowSeedWords()
|
|
|
|
var idmgmt = this._idmgmt
|
|
|
|
var shouldShow = showSeed && !!idmgmt
|
|
|
|
var seedWords = shouldShow ? idmgmt.getSeed() : null
|
|
|
|
return seedWords
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.getSelectedAddress = function () {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-04-25 23:14:34 +02:00
|
|
|
return configManager.getSelectedAccount()
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.setSelectedAddress = function (address, cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-04-04 21:13:30 +02:00
|
|
|
if (!address) {
|
|
|
|
var addresses = this._getAddresses()
|
|
|
|
address = addresses[0]
|
|
|
|
}
|
|
|
|
|
2016-04-25 23:14:34 +02:00
|
|
|
configManager.setSelectedAccount(address)
|
2016-05-13 10:13:14 +02:00
|
|
|
if (cb) return cb(null, address)
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.revealAccount = function (cb) {
|
2016-05-20 21:40:44 +02:00
|
|
|
const derivedKey = this._idmgmt.derivedKey
|
|
|
|
const keyStore = this._keyStore
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-05-20 21:40:44 +02:00
|
|
|
|
|
|
|
keyStore.setDefaultHdDerivationPath(this.hdPathString)
|
|
|
|
keyStore.generateNewAddress(derivedKey, 1)
|
|
|
|
configManager.setWallet(keyStore.serialize())
|
|
|
|
|
|
|
|
this._loadIdentities()
|
|
|
|
this._didUpdate()
|
|
|
|
cb(null)
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.getNetwork = function (err) {
|
2016-06-04 00:18:20 +02:00
|
|
|
if (err) {
|
|
|
|
this._currentState.network = 'loading'
|
2016-06-03 22:58:09 +02:00
|
|
|
this._didUpdate()
|
2016-05-30 20:22:19 +02:00
|
|
|
}
|
2016-06-04 00:18:20 +02:00
|
|
|
|
2016-04-28 03:04:33 +02:00
|
|
|
this.web3.version.getNetwork((err, network) => {
|
|
|
|
if (err) {
|
2016-06-04 00:18:20 +02:00
|
|
|
this._currentState.network = 'loading'
|
|
|
|
return this._didUpdate()
|
2016-04-28 03:04:33 +02:00
|
|
|
}
|
2016-08-12 04:44:59 +02:00
|
|
|
if (global.METAMASK_DEBUG) {
|
|
|
|
console.log('web3.getNetwork returned ' + network)
|
|
|
|
}
|
2016-04-28 03:04:33 +02:00
|
|
|
this._currentState.network = network
|
2016-06-03 22:58:09 +02:00
|
|
|
this._didUpdate()
|
2016-04-28 03:04:33 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.setLocked = function (cb) {
|
2016-03-25 22:51:19 +01:00
|
|
|
delete this._keyStore
|
|
|
|
delete this._idmgmt
|
2016-03-24 19:05:42 +01:00
|
|
|
cb()
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.submitPassword = function (password, cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-06-03 01:52:18 +02:00
|
|
|
this.tryPassword(password, (err) => {
|
2016-02-11 02:44:46 +01:00
|
|
|
if (err) return cb(err)
|
|
|
|
// load identities before returning...
|
2016-04-25 23:14:34 +02:00
|
|
|
this._loadIdentities()
|
|
|
|
cb(null, configManager.getSelectedAccount())
|
2016-02-11 02:44:46 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.exportAccount = function (address, cb) {
|
2016-04-06 21:01:10 +02:00
|
|
|
var privateKey = this._idmgmt.exportPrivateKey(address)
|
|
|
|
cb(null, privateKey)
|
|
|
|
}
|
|
|
|
|
2016-04-28 23:16:24 +02:00
|
|
|
//
|
|
|
|
// Transactions
|
|
|
|
//
|
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
// comes from dapp via zero-client hooked-wallet provider
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDoneCb, cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-06-17 04:51:34 +02:00
|
|
|
var self = this
|
2016-03-03 07:58:23 +01:00
|
|
|
// create txData obj with parameters and meta data
|
|
|
|
var time = (new Date()).getTime()
|
|
|
|
var txId = createId()
|
2016-04-20 18:29:37 +02:00
|
|
|
txParams.metamaskId = txId
|
2016-06-17 04:51:34 +02:00
|
|
|
txParams.metamaskNetworkId = self._currentState.network
|
2016-03-03 07:58:23 +01:00
|
|
|
var txData = {
|
|
|
|
id: txId,
|
|
|
|
txParams: txParams,
|
|
|
|
time: time,
|
|
|
|
status: 'unconfirmed',
|
|
|
|
}
|
2016-07-21 22:41:10 +02:00
|
|
|
|
2016-03-25 22:51:19 +01:00
|
|
|
console.log('addUnconfirmedTransaction:', txData)
|
2016-02-11 02:44:46 +01:00
|
|
|
|
2016-06-17 04:51:34 +02:00
|
|
|
// keep the onTxDoneCb around for after approval/denial (requires user interaction)
|
|
|
|
// This onTxDoneCb fires completion to the Dapp's write operation.
|
|
|
|
self._unconfTxCbs[txId] = onTxDoneCb
|
|
|
|
|
|
|
|
var provider = self._ethStore._query.currentProvider
|
2016-07-14 08:39:44 +02:00
|
|
|
var query = new EthQuery(provider)
|
|
|
|
|
|
|
|
// calculate metadata for tx
|
|
|
|
async.parallel([
|
|
|
|
analyzeForDelegateCall,
|
|
|
|
estimateGas,
|
|
|
|
], didComplete)
|
|
|
|
|
|
|
|
// perform static analyis on the target contract code
|
|
|
|
function analyzeForDelegateCall(cb){
|
|
|
|
if (txParams.to) {
|
|
|
|
query.getCode(txParams.to, function (err, result) {
|
|
|
|
if (err) return cb(err)
|
|
|
|
var code = ethUtil.toBuffer(result)
|
|
|
|
if (code !== '0x') {
|
|
|
|
var ops = ethBinToOps(code)
|
|
|
|
var containsDelegateCall = ops.some((op) => op.name === 'DELEGATECALL')
|
|
|
|
txData.containsDelegateCall = containsDelegateCall
|
|
|
|
cb()
|
|
|
|
} else {
|
|
|
|
cb()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
cb()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function estimateGas(cb){
|
|
|
|
query.estimateGas(txParams, function(err, result){
|
|
|
|
if (err) return cb(err)
|
|
|
|
txData.estimatedGas = result
|
|
|
|
cb()
|
2016-06-17 04:51:34 +02:00
|
|
|
})
|
|
|
|
}
|
2016-02-11 02:44:46 +01:00
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
function didComplete (err) {
|
2016-06-17 04:51:34 +02:00
|
|
|
if (err) return cb(err)
|
2016-07-14 08:39:44 +02:00
|
|
|
configManager.addTx(txData)
|
2016-06-17 04:51:34 +02:00
|
|
|
// signal update
|
|
|
|
self._didUpdate()
|
|
|
|
// signal completion of add tx
|
|
|
|
cb(null, txData)
|
|
|
|
}
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
// comes from metamask ui
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.approveTransaction = function (txId, cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-25 22:51:19 +01:00
|
|
|
var approvalCb = this._unconfTxCbs[txId] || noop
|
2016-02-22 21:10:25 +01:00
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
// accept tx
|
2016-02-13 02:57:10 +01:00
|
|
|
cb()
|
2016-03-03 07:58:23 +01:00
|
|
|
approvalCb(null, true)
|
|
|
|
// clean up
|
2016-04-19 01:39:35 +02:00
|
|
|
configManager.confirmTx(txId)
|
2016-03-25 22:51:19 +01:00
|
|
|
delete this._unconfTxCbs[txId]
|
|
|
|
this._didUpdate()
|
2016-02-13 02:57:10 +01:00
|
|
|
}
|
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
// comes from metamask ui
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.cancelTransaction = function (txId) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-25 22:51:19 +01:00
|
|
|
var approvalCb = this._unconfTxCbs[txId] || noop
|
2016-03-03 07:58:23 +01:00
|
|
|
|
|
|
|
// reject tx
|
|
|
|
approvalCb(null, false)
|
|
|
|
// clean up
|
2016-04-19 01:39:35 +02:00
|
|
|
configManager.rejectTx(txId)
|
2016-03-25 22:51:19 +01:00
|
|
|
delete this._unconfTxCbs[txId]
|
|
|
|
this._didUpdate()
|
2016-02-12 21:55:20 +01:00
|
|
|
}
|
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
// performs the actual signing, no autofill of params
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.signTransaction = function (txParams, cb) {
|
2016-02-11 02:44:46 +01:00
|
|
|
try {
|
2016-03-03 07:58:23 +01:00
|
|
|
console.log('signing tx...', txParams)
|
2016-03-25 22:51:19 +01:00
|
|
|
var rawTx = this._idmgmt.signTx(txParams)
|
2016-03-03 07:58:23 +01:00
|
|
|
cb(null, rawTx)
|
2016-02-11 02:44:46 +01:00
|
|
|
} catch (err) {
|
|
|
|
cb(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 23:16:24 +02:00
|
|
|
//
|
|
|
|
// Messages
|
|
|
|
//
|
|
|
|
|
|
|
|
// comes from dapp via zero-client hooked-wallet provider
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.addUnconfirmedMessage = function (msgParams, cb) {
|
2016-04-28 23:16:24 +02:00
|
|
|
// create txData obj with parameters and meta data
|
|
|
|
var time = (new Date()).getTime()
|
|
|
|
var msgId = createId()
|
|
|
|
var msgData = {
|
|
|
|
id: msgId,
|
|
|
|
msgParams: msgParams,
|
|
|
|
time: time,
|
|
|
|
status: 'unconfirmed',
|
|
|
|
}
|
2016-05-03 23:32:22 +02:00
|
|
|
messageManager.addMsg(msgData)
|
2016-04-28 23:16:24 +02:00
|
|
|
console.log('addUnconfirmedMessage:', msgData)
|
|
|
|
|
|
|
|
// keep the cb around for after approval (requires user interaction)
|
|
|
|
// This cb fires completion to the Dapp's write operation.
|
|
|
|
this._unconfMsgCbs[msgId] = cb
|
|
|
|
|
|
|
|
// signal update
|
|
|
|
this._didUpdate()
|
|
|
|
|
|
|
|
return msgId
|
|
|
|
}
|
|
|
|
|
|
|
|
// comes from metamask ui
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.approveMessage = function (msgId, cb) {
|
2016-04-28 23:16:24 +02:00
|
|
|
var approvalCb = this._unconfMsgCbs[msgId] || noop
|
|
|
|
|
|
|
|
// accept msg
|
|
|
|
cb()
|
|
|
|
approvalCb(null, true)
|
|
|
|
// clean up
|
2016-05-03 23:32:22 +02:00
|
|
|
messageManager.confirmMsg(msgId)
|
2016-04-28 23:16:24 +02:00
|
|
|
delete this._unconfMsgCbs[msgId]
|
|
|
|
this._didUpdate()
|
|
|
|
}
|
|
|
|
|
|
|
|
// comes from metamask ui
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.cancelMessage = function (msgId) {
|
2016-04-28 23:16:24 +02:00
|
|
|
var approvalCb = this._unconfMsgCbs[msgId] || noop
|
|
|
|
|
|
|
|
// reject tx
|
|
|
|
approvalCb(null, false)
|
|
|
|
// clean up
|
2016-05-03 23:32:22 +02:00
|
|
|
messageManager.rejectMsg(msgId)
|
2016-04-28 23:16:24 +02:00
|
|
|
delete this._unconfTxCbs[msgId]
|
|
|
|
this._didUpdate()
|
|
|
|
}
|
|
|
|
|
|
|
|
// performs the actual signing, no autofill of params
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.signMessage = function (msgParams, cb) {
|
2016-04-28 23:16:24 +02:00
|
|
|
try {
|
|
|
|
console.log('signing msg...', msgParams.data)
|
|
|
|
var rawMsg = this._idmgmt.signMsg(msgParams.from, msgParams.data)
|
2016-05-03 23:32:22 +02:00
|
|
|
if ('metamaskId' in msgParams) {
|
|
|
|
var id = msgParams.metamaskId
|
|
|
|
delete msgParams.metamaskId
|
|
|
|
|
|
|
|
this.approveMessage(id, cb)
|
|
|
|
} else {
|
|
|
|
cb(null, rawMsg)
|
|
|
|
}
|
2016-04-28 23:16:24 +02:00
|
|
|
} catch (err) {
|
|
|
|
cb(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
//
|
|
|
|
// private
|
|
|
|
//
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._didUpdate = function () {
|
2016-03-25 22:51:19 +01:00
|
|
|
this.emit('update', this.getState())
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._isUnlocked = function () {
|
2016-03-25 22:51:19 +01:00
|
|
|
var result = Boolean(this._keyStore) && Boolean(this._idmgmt)
|
2016-02-11 02:44:46 +01:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-02-17 09:55:57 +01:00
|
|
|
// load identities from keyStoreet
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._loadIdentities = function () {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-25 22:51:19 +01:00
|
|
|
if (!this._isUnlocked()) throw new Error('not unlocked')
|
2016-03-29 21:21:46 +02:00
|
|
|
|
|
|
|
var addresses = this._getAddresses()
|
|
|
|
addresses.forEach((address, i) => {
|
2016-02-17 09:55:57 +01:00
|
|
|
// // add to ethStore
|
2016-03-25 22:51:19 +01:00
|
|
|
this._ethStore.addAccount(address)
|
2016-02-11 02:44:46 +01:00
|
|
|
// add to identities
|
2016-06-21 22:18:32 +02:00
|
|
|
const defaultLabel = 'Wallet ' + (i + 1)
|
2016-05-21 01:18:54 +02:00
|
|
|
const nickname = configManager.nicknameForWallet(address)
|
2016-02-11 02:44:46 +01:00
|
|
|
var identity = {
|
2016-05-21 01:18:54 +02:00
|
|
|
name: nickname || defaultLabel,
|
2016-02-11 02:44:46 +01:00
|
|
|
address: address,
|
2016-04-05 00:35:41 +02:00
|
|
|
mayBeFauceting: this._mayBeFauceting(i),
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
2016-03-25 22:51:19 +01:00
|
|
|
this._currentState.identities[address] = identity
|
2016-02-11 02:44:46 +01:00
|
|
|
})
|
2016-03-25 22:51:19 +01:00
|
|
|
this._didUpdate()
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.saveAccountLabel = function (account, label, cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-05-21 01:18:54 +02:00
|
|
|
configManager.setNicknameForWallet(account, label)
|
|
|
|
this._loadIdentities()
|
|
|
|
cb(null, label)
|
|
|
|
this._didUpdate()
|
|
|
|
}
|
|
|
|
|
2016-04-05 00:35:41 +02:00
|
|
|
// mayBeFauceting
|
|
|
|
// If on testnet, index 0 may be fauceting.
|
|
|
|
// The UI will have to check the balance to know.
|
|
|
|
// If there is no balance and it mayBeFauceting,
|
|
|
|
// then it is in fact fauceting.
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._mayBeFauceting = function (i) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-04-05 00:35:41 +02:00
|
|
|
var config = configManager.getProvider()
|
|
|
|
if (i === 0 &&
|
|
|
|
config.type === 'rpc' &&
|
|
|
|
config.rpcTarget === DEFAULT_RPC) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-02-11 02:44:46 +01:00
|
|
|
//
|
|
|
|
// keyStore managment - unlocking + deserialization
|
|
|
|
//
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype.tryPassword = function (password, cb) {
|
2016-03-25 22:51:19 +01:00
|
|
|
this._createIdmgmt(password, null, null, cb)
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._createIdmgmt = function (password, seed, entropy, cb) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-02-11 02:44:46 +01:00
|
|
|
var keyStore = null
|
2016-03-24 18:32:50 +01:00
|
|
|
LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
|
2016-03-03 07:58:23 +01:00
|
|
|
if (err) return cb(err)
|
Made configuration migrateable
Abstract all configuration data into a singleton called `configManager`, who is responsible for reading and writing to the persisted storage (localStorage, in our case).
Uses my new module [pojo-migrator](https://www.npmjs.com/package/pojo-migrator), and wraps it with the `ConfigManager` class, which we can hang any state setting or getting methods we need.
By keeping all the persisted state in one place, we can stabilize its outward-facing API, making the interactions increasingly atomic, which will allow us to add features that require restructuring the persisted data in the long term without having to rewrite UI or even `background.js` code.
All the restructuring and data-type management is kept in one neat little place.
This should make it very easy to add new configuration options like user-configured providers, per-domain vaults, and more!
I know this doesn't seem like a big user-facing feature, but we have a big laundry list of features that I think this will really help streamline.
2016-03-31 04:15:49 +02:00
|
|
|
var serializedKeystore = configManager.getWallet()
|
2016-03-24 18:32:50 +01:00
|
|
|
|
2016-03-15 21:39:12 +01:00
|
|
|
if (seed) {
|
2016-04-01 23:02:02 +02:00
|
|
|
try {
|
|
|
|
keyStore = this._restoreFromSeed(password, seed, derivedKey)
|
|
|
|
} catch (e) {
|
|
|
|
return cb(e)
|
|
|
|
}
|
2016-03-24 18:32:50 +01:00
|
|
|
|
Made configuration migrateable
Abstract all configuration data into a singleton called `configManager`, who is responsible for reading and writing to the persisted storage (localStorage, in our case).
Uses my new module [pojo-migrator](https://www.npmjs.com/package/pojo-migrator), and wraps it with the `ConfigManager` class, which we can hang any state setting or getting methods we need.
By keeping all the persisted state in one place, we can stabilize its outward-facing API, making the interactions increasingly atomic, which will allow us to add features that require restructuring the persisted data in the long term without having to rewrite UI or even `background.js` code.
All the restructuring and data-type management is kept in one neat little place.
This should make it very easy to add new configuration options like user-configured providers, per-domain vaults, and more!
I know this doesn't seem like a big user-facing feature, but we have a big laundry list of features that I think this will really help streamline.
2016-03-31 04:15:49 +02:00
|
|
|
// returning user, recovering from storage
|
2016-03-15 21:39:12 +01:00
|
|
|
} else if (serializedKeystore) {
|
2016-03-31 19:24:39 +02:00
|
|
|
keyStore = LightwalletKeyStore.deserialize(serializedKeystore)
|
2016-03-24 18:32:50 +01:00
|
|
|
var isCorrect = keyStore.isDerivedKeyCorrect(derivedKey)
|
2016-03-03 07:58:23 +01:00
|
|
|
if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
|
2016-03-24 18:32:50 +01:00
|
|
|
|
|
|
|
// first time here
|
2016-03-03 07:58:23 +01:00
|
|
|
} else {
|
2016-03-24 18:32:50 +01:00
|
|
|
keyStore = this._createFirstWallet(entropy, derivedKey)
|
2016-03-03 07:58:23 +01:00
|
|
|
}
|
2016-03-24 18:32:50 +01:00
|
|
|
|
|
|
|
this._keyStore = keyStore
|
|
|
|
this._idmgmt = new IdManagement({
|
|
|
|
keyStore: keyStore,
|
|
|
|
derivedKey: derivedKey,
|
2016-03-25 23:38:08 +01:00
|
|
|
hdPathSTring: this.hdPathString,
|
2016-06-25 01:13:27 +02:00
|
|
|
configManager: this.configManager,
|
2016-03-24 18:32:50 +01:00
|
|
|
})
|
|
|
|
|
2016-03-03 07:58:23 +01:00
|
|
|
cb()
|
|
|
|
})
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._restoreFromSeed = function (password, seed, derivedKey) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-25 22:51:19 +01:00
|
|
|
var keyStore = new LightwalletKeyStore(seed, derivedKey, this.hdPathString)
|
2016-06-21 22:18:32 +02:00
|
|
|
keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
|
2016-03-25 23:38:08 +01:00
|
|
|
keyStore.setDefaultHdDerivationPath(this.hdPathString)
|
|
|
|
|
|
|
|
keyStore.generateNewAddress(derivedKey, 3)
|
Made configuration migrateable
Abstract all configuration data into a singleton called `configManager`, who is responsible for reading and writing to the persisted storage (localStorage, in our case).
Uses my new module [pojo-migrator](https://www.npmjs.com/package/pojo-migrator), and wraps it with the `ConfigManager` class, which we can hang any state setting or getting methods we need.
By keeping all the persisted state in one place, we can stabilize its outward-facing API, making the interactions increasingly atomic, which will allow us to add features that require restructuring the persisted data in the long term without having to rewrite UI or even `background.js` code.
All the restructuring and data-type management is kept in one neat little place.
This should make it very easy to add new configuration options like user-configured providers, per-domain vaults, and more!
I know this doesn't seem like a big user-facing feature, but we have a big laundry list of features that I think this will really help streamline.
2016-03-31 04:15:49 +02:00
|
|
|
configManager.setWallet(keyStore.serialize())
|
2016-08-12 04:44:59 +02:00
|
|
|
if (global.METAMASK_DEBUG) {
|
|
|
|
console.log('restored from seed. saved to keystore')
|
|
|
|
}
|
2016-03-25 22:51:19 +01:00
|
|
|
return keyStore
|
2016-03-24 18:32:50 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._createFirstWallet = function (entropy, derivedKey) {
|
2016-06-25 00:52:56 +02:00
|
|
|
const configManager = this.configManager
|
2016-03-24 18:32:50 +01:00
|
|
|
var secretSeed = LightwalletKeyStore.generateRandomSeed(entropy)
|
2016-03-25 20:41:18 +01:00
|
|
|
var keyStore = new LightwalletKeyStore(secretSeed, derivedKey, this.hdPathString)
|
2016-06-21 22:18:32 +02:00
|
|
|
keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
|
2016-03-25 23:38:08 +01:00
|
|
|
keyStore.setDefaultHdDerivationPath(this.hdPathString)
|
|
|
|
|
2016-07-12 21:06:18 +02:00
|
|
|
keyStore.generateNewAddress(derivedKey, 1)
|
Made configuration migrateable
Abstract all configuration data into a singleton called `configManager`, who is responsible for reading and writing to the persisted storage (localStorage, in our case).
Uses my new module [pojo-migrator](https://www.npmjs.com/package/pojo-migrator), and wraps it with the `ConfigManager` class, which we can hang any state setting or getting methods we need.
By keeping all the persisted state in one place, we can stabilize its outward-facing API, making the interactions increasingly atomic, which will allow us to add features that require restructuring the persisted data in the long term without having to rewrite UI or even `background.js` code.
All the restructuring and data-type management is kept in one neat little place.
This should make it very easy to add new configuration options like user-configured providers, per-domain vaults, and more!
I know this doesn't seem like a big user-facing feature, but we have a big laundry list of features that I think this will really help streamline.
2016-03-31 04:15:49 +02:00
|
|
|
configManager.setWallet(keyStore.serialize())
|
|
|
|
console.log('saved to keystore')
|
2016-03-24 18:32:50 +01:00
|
|
|
return keyStore
|
|
|
|
}
|
|
|
|
|
2016-03-29 21:21:46 +02:00
|
|
|
// get addresses and normalize address hexString
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._getAddresses = function () {
|
|
|
|
return this._keyStore.getAddresses(this.hdPathString).map((address) => { return '0x' + address })
|
2016-03-29 21:21:46 +02:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
IdentityStore.prototype._autoFaucet = function () {
|
2016-03-29 21:21:46 +02:00
|
|
|
var addresses = this._getAddresses()
|
|
|
|
autoFaucet(addresses[0])
|
|
|
|
}
|
|
|
|
|
2016-02-11 02:44:46 +01:00
|
|
|
// util
|
|
|
|
|
2016-06-21 22:18:32 +02:00
|
|
|
function noop () {}
|