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-10-17 21:47:37 +02:00
|
|
|
const BN = ethUtil.BN
|
2016-07-14 08:39:44 +02:00
|
|
|
const EthQuery = require('eth-query')
|
2016-09-10 04:42:18 +02:00
|
|
|
const KeyStore = require('eth-lightwallet').keystore
|
2016-02-11 02:44:46 +01:00
|
|
|
const clone = require('clone')
|
|
|
|
const extend = require('xtend')
|
2016-11-17 23:05:12 +01:00
|
|
|
const createId = require('./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-08-24 02:17:08 +02:00
|
|
|
var serializedKeystore = this.configManager.getWallet()
|
2016-06-25 01:13:27 +02:00
|
|
|
|
2016-08-24 02:17:08 +02:00
|
|
|
if (serializedKeystore) {
|
|
|
|
this.configManager.setData({})
|
|
|
|
}
|
2016-09-10 04:42:18 +02:00
|
|
|
|
2016-09-13 00:18:32 +02:00
|
|
|
this.purgeCache()
|
2016-09-12 20:07:25 +02:00
|
|
|
this._createVault(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-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-09-10 04:42:18 +02:00
|
|
|
|
2016-10-12 00:29:12 +02:00
|
|
|
this._loadIdentities()
|
|
|
|
|
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-09-13 00:18:32 +02:00
|
|
|
this.purgeCache()
|
|
|
|
|
2016-09-12 20:07:25 +02:00
|
|
|
this._createVault(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()
|
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-10-13 04:35:09 +02:00
|
|
|
gasMultiplier: configManager.getGasMultiplier(),
|
|
|
|
|
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-09-12 17:50:42 +02:00
|
|
|
IdentityStore.prototype.setSelectedAddressSync = function (address) {
|
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-09-12 17:50:42 +02:00
|
|
|
return address
|
|
|
|
}
|
|
|
|
|
|
|
|
IdentityStore.prototype.setSelectedAddress = function (address, cb) {
|
|
|
|
const resultAddress = this.setSelectedAddressSync(address)
|
|
|
|
if (cb) return cb(null, resultAddress)
|
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)
|
2016-09-12 23:34:45 +02:00
|
|
|
const addresses = keyStore.getAddresses()
|
|
|
|
const address = addresses[ addresses.length - 1 ]
|
2016-09-12 23:22:06 +02:00
|
|
|
|
|
|
|
this._ethStore.addAccount(ethUtil.addHexPrefix(address))
|
2016-09-10 04:42:18 +02:00
|
|
|
|
2016-05-20 21:40:44 +02:00
|
|
|
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-10-13 04:35:09 +02:00
|
|
|
|
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-10-13 04:35:09 +02:00
|
|
|
gasMultiplier: configManager.getGasMultiplier() || 1,
|
2016-03-03 07:58:23 +01:00
|
|
|
}
|
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) {
|
2016-10-25 23:58:04 +02:00
|
|
|
query.getCode(txParams.to, (err, result) => {
|
2016-11-14 20:17:53 +01:00
|
|
|
if (err) return cb(err.message || err)
|
2016-10-26 02:41:50 +02:00
|
|
|
var containsDelegateCall = self.checkForDelegateCall(result)
|
2016-10-17 23:48:25 +02:00
|
|
|
txData.containsDelegateCall = containsDelegateCall
|
|
|
|
cb()
|
2016-07-14 08:39:44 +02:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
cb()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function estimateGas(cb){
|
2016-11-11 06:19:34 +01:00
|
|
|
var estimationParams = extend(txParams)
|
2016-11-22 21:10:49 +01:00
|
|
|
query.getBlockByNumber('latest', true, function(err, block){
|
|
|
|
if (err) return cb(err)
|
|
|
|
// check if gasLimit is already specified
|
|
|
|
const gasLimitSpecified = Boolean(txParams.gas)
|
|
|
|
// if not, fallback to block gasLimit
|
|
|
|
if (!gasLimitSpecified) {
|
|
|
|
estimationParams.gas = block.gasLimit
|
|
|
|
}
|
|
|
|
// run tx, see if it will OOG
|
|
|
|
query.estimateGas(estimationParams, function(err, estimatedGasHex){
|
|
|
|
if (err) return cb(err.message || err)
|
|
|
|
// all gas used - must be an error
|
|
|
|
if (estimatedGasHex === estimationParams.gas) {
|
|
|
|
txData.simulationFails = true
|
|
|
|
txData.estimatedGas = estimatedGasHex
|
|
|
|
txData.txParams.gas = estimatedGasHex
|
|
|
|
cb()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// otherwise, did not use all gas, must be ok
|
|
|
|
|
|
|
|
// if specified gasLimit and no error, we're done
|
|
|
|
if (gasLimitSpecified) {
|
|
|
|
txData.estimatedGas = txParams.gas
|
2016-11-11 06:19:34 +01:00
|
|
|
cb()
|
2016-11-22 21:10:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// try adding an additional gas buffer to our estimation for safety
|
|
|
|
const estimatedGasBn = new BN(ethUtil.stripHexPrefix(estimatedGasHex), 16)
|
|
|
|
const blockGasLimitBn = new BN(ethUtil.stripHexPrefix(block.gasLimit), 16)
|
|
|
|
const estimationWithBuffer = self.addGasBuffer(estimatedGasBn)
|
|
|
|
// added gas buffer is too high
|
|
|
|
if (estimationWithBuffer.gt(blockGasLimitBn)) {
|
|
|
|
txData.estimatedGas = estimatedGasHex
|
|
|
|
txData.txParams.gas = estimatedGasHex
|
|
|
|
// added gas buffer is safe
|
|
|
|
} else {
|
|
|
|
const gasWithBufferHex = ethUtil.intToHex(estimationWithBuffer)
|
|
|
|
txData.estimatedGas = gasWithBufferHex
|
|
|
|
txData.txParams.gas = gasWithBufferHex
|
|
|
|
}
|
|
|
|
cb()
|
2016-11-11 06:19:34 +01:00
|
|
|
return
|
2016-11-22 21:10:49 +01:00
|
|
|
})
|
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-11-14 20:17:53 +01:00
|
|
|
if (err) return cb(err.message || 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-10-17 23:48:25 +02:00
|
|
|
IdentityStore.prototype.checkForDelegateCall = function (codeHex) {
|
|
|
|
const code = ethUtil.toBuffer(codeHex)
|
|
|
|
if (code !== '0x') {
|
|
|
|
const ops = ethBinToOps(code)
|
|
|
|
const containsDelegateCall = ops.some((op) => op.name === 'DELEGATECALL')
|
|
|
|
return containsDelegateCall
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-22 21:10:49 +01:00
|
|
|
IdentityStore.prototype.addGasBuffer = function (gasBn) {
|
|
|
|
// add 20% to specified gas
|
|
|
|
const gasBuffer = gasBn.div(new BN('5', 10))
|
|
|
|
const gasWithBuffer = gasBn.add(gasBuffer)
|
|
|
|
return gasWithBuffer
|
2016-10-17 21:47:37 +02: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-09-12 23:33:54 +02:00
|
|
|
this._ethStore.addAccount(ethUtil.addHexPrefix(address))
|
2016-02-11 02:44:46 +01:00
|
|
|
// add to identities
|
2016-10-26 01:54:43 +02:00
|
|
|
const defaultLabel = 'Account ' + (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)
|
|
|
|
}
|
|
|
|
|
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-09-10 04:42:18 +02:00
|
|
|
var serializedKeystore = this.configManager.getWallet()
|
|
|
|
var keyStore = KeyStore.deserialize(serializedKeystore)
|
2016-08-24 02:17:08 +02:00
|
|
|
|
2016-09-10 04:42:18 +02:00
|
|
|
keyStore.keyFromPassword(password, (err, pwDerivedKey) => {
|
2016-03-03 07:58:23 +01:00
|
|
|
if (err) return cb(err)
|
2016-03-24 18:32:50 +01:00
|
|
|
|
2016-09-10 04:42:18 +02:00
|
|
|
const isCorrect = keyStore.isDerivedKeyCorrect(pwDerivedKey)
|
|
|
|
if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
|
2016-03-24 18:32:50 +01:00
|
|
|
|
2016-09-12 20:25:30 +02:00
|
|
|
this._keyStore = keyStore
|
2016-09-12 20:21:27 +02:00
|
|
|
this._createIdMgmt(pwDerivedKey)
|
2016-03-03 07:58:23 +01:00
|
|
|
cb()
|
|
|
|
})
|
2016-02-11 02:44:46 +01:00
|
|
|
}
|
|
|
|
|
2016-09-12 20:07:25 +02:00
|
|
|
IdentityStore.prototype._createVault = function (password, seedPhrase, entropy, cb) {
|
2016-09-10 21:08:27 +02:00
|
|
|
const opts = {
|
|
|
|
password,
|
|
|
|
hdPathString: this.hdPathString,
|
|
|
|
}
|
|
|
|
|
2016-09-10 04:42:18 +02:00
|
|
|
if (seedPhrase) {
|
|
|
|
opts.seedPhrase = seedPhrase
|
2016-08-12 04:44:59 +02:00
|
|
|
}
|
2016-09-10 04:42:18 +02:00
|
|
|
|
|
|
|
KeyStore.createVault(opts, (err, keyStore) => {
|
|
|
|
if (err) return cb(err)
|
|
|
|
|
|
|
|
this._keyStore = keyStore
|
|
|
|
|
|
|
|
keyStore.keyFromPassword(password, (err, derivedKey) => {
|
|
|
|
if (err) return cb(err)
|
|
|
|
|
2016-09-10 21:08:27 +02:00
|
|
|
this.purgeCache()
|
2016-09-10 20:46:50 +02:00
|
|
|
|
2016-09-10 04:42:18 +02:00
|
|
|
keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
|
|
|
|
|
|
|
|
this._createFirstWallet(derivedKey)
|
2016-09-12 20:07:25 +02:00
|
|
|
this._createIdMgmt(derivedKey)
|
2016-09-12 17:50:42 +02:00
|
|
|
this.setSelectedAddressSync()
|
|
|
|
|
2016-09-10 04:42:18 +02:00
|
|
|
cb()
|
|
|
|
})
|
|
|
|
})
|
2016-03-24 18:32:50 +01:00
|
|
|
}
|
|
|
|
|
2016-09-12 20:07:25 +02:00
|
|
|
IdentityStore.prototype._createIdMgmt = function (derivedKey) {
|
|
|
|
this._idmgmt = new IdManagement({
|
2016-09-12 20:21:27 +02:00
|
|
|
keyStore: this._keyStore,
|
2016-09-12 20:07:25 +02:00
|
|
|
derivedKey: derivedKey,
|
|
|
|
configManager: this.configManager,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-09-10 21:08:27 +02:00
|
|
|
IdentityStore.prototype.purgeCache = function () {
|
2016-09-13 00:18:32 +02:00
|
|
|
this._currentState.identities = {}
|
2016-09-13 00:26:07 +02:00
|
|
|
let accounts
|
|
|
|
try {
|
2016-09-13 07:13:52 +02:00
|
|
|
accounts = Object.keys(this._ethStore._currentState.accounts)
|
2016-09-13 00:26:07 +02:00
|
|
|
} catch (e) {
|
|
|
|
accounts = []
|
|
|
|
}
|
2016-09-13 00:18:32 +02:00
|
|
|
accounts.forEach((address) => {
|
|
|
|
this._ethStore.removeAccount(address)
|
2016-09-10 21:08:27 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-09-10 04:42:18 +02:00
|
|
|
IdentityStore.prototype._createFirstWallet = function (derivedKey) {
|
|
|
|
const keyStore = this._keyStore
|
2016-03-25 23:38:08 +01:00
|
|
|
keyStore.setDefaultHdDerivationPath(this.hdPathString)
|
2016-09-10 21:08:27 +02:00
|
|
|
keyStore.generateNewAddress(derivedKey, 1)
|
2016-09-11 00:45:34 +02:00
|
|
|
this.configManager.setWallet(keyStore.serialize())
|
2016-09-10 04:42:18 +02:00
|
|
|
var addresses = keyStore.getAddresses()
|
2016-09-12 17:39:46 +02:00
|
|
|
this._ethStore.addAccount(ethUtil.addHexPrefix(addresses[0]))
|
2016-03-24 18:32:50 +01:00
|
|
|
}
|
|
|
|
|
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 () {
|
2016-09-10 21:08:27 +02:00
|
|
|
return this._keyStore.getAddresses(this.hdPathString).map((address) => {
|
|
|
|
return ethUtil.addHexPrefix(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 () {}
|