mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
idmgmt - refactor
This commit is contained in:
parent
31c9bf3c26
commit
f8c5b90320
@ -1,27 +1,18 @@
|
|||||||
const Dnode = require('dnode')
|
const Dnode = require('dnode')
|
||||||
|
const eos = require('end-of-stream')
|
||||||
|
const extend = require('xtend')
|
||||||
|
const EthStore = require('eth-store')
|
||||||
const PortStream = require('./lib/port-stream.js')
|
const PortStream = require('./lib/port-stream.js')
|
||||||
const MetaMaskProvider = require('./lib/metamask-provider')
|
const MetaMaskProvider = require('./lib/metamask-provider')
|
||||||
const IdentityManager = require('./lib/idmgmt')
|
// const IdentityManager = require('./lib/idmgmt')
|
||||||
const eos = require('end-of-stream')
|
const IdentityStore = require('./lib/idStore')
|
||||||
|
|
||||||
console.log('ready to roll')
|
console.log('ready to roll')
|
||||||
|
|
||||||
var wallet = new IdentityManager()
|
//
|
||||||
|
// connect to other contexts
|
||||||
|
//
|
||||||
|
|
||||||
// setup provider
|
|
||||||
var zeroClient = MetaMaskProvider({
|
|
||||||
rpcUrl: 'https://rawtestrpc.metamask.io/',
|
|
||||||
getAccounts: wallet.getAccounts.bind(wallet),
|
|
||||||
signTransaction: wallet.addUnconfirmedTransaction.bind(wallet),
|
|
||||||
})
|
|
||||||
|
|
||||||
wallet.setProvider(zeroClient)
|
|
||||||
zeroClient.on('block', function(block){
|
|
||||||
wallet.newBlock(block)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// setup messaging
|
|
||||||
chrome.runtime.onConnect.addListener(connectRemote)
|
chrome.runtime.onConnect.addListener(connectRemote)
|
||||||
function connectRemote(remotePort){
|
function connectRemote(remotePort){
|
||||||
var isMetaMaskInternalProcess = (remotePort.name === 'popup')
|
var isMetaMaskInternalProcess = (remotePort.name === 'popup')
|
||||||
@ -34,42 +25,33 @@ function connectRemote(remotePort){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInternalCommunication(remotePort){
|
|
||||||
var duplex = new PortStream(remotePort)
|
|
||||||
var connection = Dnode({
|
|
||||||
// this is annoying, have to decompose wallet
|
|
||||||
getState: wallet.getState.bind(wallet),
|
|
||||||
submitPassword: wallet.submitPassword.bind(wallet),
|
|
||||||
setSelectedAddress: wallet.setSelectedAddress.bind(wallet),
|
|
||||||
signTransaction: wallet.signTransaction.bind(wallet),
|
|
||||||
setLocked: wallet.setLocked.bind(wallet),
|
|
||||||
getAccounts: wallet.getAccounts.bind(wallet),
|
|
||||||
newBlock: wallet.newBlock.bind(wallet),
|
|
||||||
setProvider: wallet.setProvider.bind(wallet),
|
|
||||||
})
|
|
||||||
duplex.pipe(connection).pipe(duplex)
|
|
||||||
connection.on('remote', function(remote){
|
|
||||||
|
|
||||||
// push updates to popup
|
|
||||||
wallet.on('update', sendUpdate)
|
|
||||||
eos(duplex, function unsubscribe(){
|
|
||||||
wallet.removeListener('update', sendUpdate)
|
|
||||||
})
|
|
||||||
function sendUpdate(state){
|
|
||||||
remote.sendUpdate(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// sub to metamask store
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleExternalCommunication(remotePort){
|
function handleExternalCommunication(remotePort){
|
||||||
remotePort.onMessage.addListener(onRpcRequest.bind(null, remotePort))
|
remotePort.onMessage.addListener(onRpcRequest.bind(null, remotePort))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// state and network
|
||||||
|
//
|
||||||
|
|
||||||
|
var idStore = new IdentityStore()
|
||||||
|
var zeroClient = MetaMaskProvider({
|
||||||
|
rpcUrl: 'https://rawtestrpc.metamask.io/',
|
||||||
|
getAccounts: function(cb){
|
||||||
|
var selectedAddress = idStore.getSelectedAddress()
|
||||||
|
var result = selectedAddress ? [selectedAddress] : []
|
||||||
|
cb(null, result)
|
||||||
|
},
|
||||||
|
signTransaction: idStore.addUnconfirmedTransaction.bind(idStore),
|
||||||
|
})
|
||||||
|
var ethStore = new EthStore(zeroClient)
|
||||||
|
idStore.setStore(ethStore)
|
||||||
|
|
||||||
|
function getState(){
|
||||||
|
var state = extend(ethStore.getState(), idStore.getState())
|
||||||
|
console.log(state)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
// handle rpc requests
|
// handle rpc requests
|
||||||
function onRpcRequest(remotePort, payload){
|
function onRpcRequest(remotePort, payload){
|
||||||
// console.log('MetaMaskPlugin - incoming payload:', payload)
|
// console.log('MetaMaskPlugin - incoming payload:', payload)
|
||||||
@ -84,8 +66,44 @@ function onRpcRequest(remotePort, payload){
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup badge text
|
|
||||||
wallet.on('update', updateBadge)
|
//
|
||||||
|
// popup integration
|
||||||
|
//
|
||||||
|
|
||||||
|
function handleInternalCommunication(remotePort){
|
||||||
|
var duplex = new PortStream(remotePort)
|
||||||
|
var connection = Dnode({
|
||||||
|
getState: function(cb){ cb(null, getState()) },
|
||||||
|
// forward directly to idStore
|
||||||
|
submitPassword: idStore.submitPassword.bind(idStore),
|
||||||
|
setSelectedAddress: idStore.setSelectedAddress.bind(idStore),
|
||||||
|
signTransaction: idStore.signTransaction.bind(idStore),
|
||||||
|
setLocked: idStore.setLocked.bind(idStore),
|
||||||
|
})
|
||||||
|
duplex.pipe(connection).pipe(duplex)
|
||||||
|
connection.on('remote', function(remote){
|
||||||
|
|
||||||
|
// push updates to popup
|
||||||
|
ethStore.on('update', sendUpdate)
|
||||||
|
idStore.on('update', sendUpdate)
|
||||||
|
// teardown on disconnect
|
||||||
|
eos(duplex, function unsubscribe(){
|
||||||
|
ethStore.removeListener('update', sendUpdate)
|
||||||
|
})
|
||||||
|
function sendUpdate(){
|
||||||
|
var state = getState()
|
||||||
|
remote.sendUpdate(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// plugin badge text
|
||||||
|
//
|
||||||
|
|
||||||
|
idStore.on('update', updateBadge)
|
||||||
|
|
||||||
function updateBadge(state){
|
function updateBadge(state){
|
||||||
var label = ''
|
var label = ''
|
||||||
@ -97,17 +115,3 @@ function updateBadge(state){
|
|||||||
chrome.browserAction.setBadgeBackgroundColor({color: '#506F8B'})
|
chrome.browserAction.setBadgeBackgroundColor({color: '#506F8B'})
|
||||||
}
|
}
|
||||||
|
|
||||||
// function handleMessage(msg){
|
|
||||||
// console.log('got message!', msg.type)
|
|
||||||
// switch(msg.type){
|
|
||||||
|
|
||||||
// case 'addUnsignedTx':
|
|
||||||
// addTransaction(msg.payload)
|
|
||||||
// return
|
|
||||||
|
|
||||||
// case 'removeUnsignedTx':
|
|
||||||
// removeTransaction(msg.payload)
|
|
||||||
// return
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
263
app/scripts/lib/idStore.js
Normal file
263
app/scripts/lib/idStore.js
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
const EventEmitter = require('events').EventEmitter
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const Transaction = require('ethereumjs-tx')
|
||||||
|
const KeyStore = require('eth-lightwallet').keystore
|
||||||
|
const async = require('async')
|
||||||
|
const clone = require('clone')
|
||||||
|
const extend = require('xtend')
|
||||||
|
const createId = require('web3-provider-engine/util/random-id')
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = IdentityStore
|
||||||
|
|
||||||
|
|
||||||
|
inherits(IdentityStore, EventEmitter)
|
||||||
|
function IdentityStore(ethStore) {
|
||||||
|
const self = this
|
||||||
|
EventEmitter.call(self)
|
||||||
|
|
||||||
|
// we just use the ethStore to auto-add accounts
|
||||||
|
self._ethStore = ethStore
|
||||||
|
|
||||||
|
self._currentState = {
|
||||||
|
selectedAddress: null,
|
||||||
|
identities: {},
|
||||||
|
unconfTxs: {},
|
||||||
|
}
|
||||||
|
// not part of serilized metamask state - only kept in memory
|
||||||
|
self._unconfTxCbs = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// public
|
||||||
|
//
|
||||||
|
|
||||||
|
IdentityStore.prototype.setStore = function(store){
|
||||||
|
const self = this
|
||||||
|
self._ethStore = store
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype.getState = function(){
|
||||||
|
const self = this
|
||||||
|
return clone(extend(self._currentState, {
|
||||||
|
isUnlocked: self._isUnlocked(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype.getSelectedAddress = function(){
|
||||||
|
const self = this
|
||||||
|
return self._currentState.selectedAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype.setSelectedAddress = function(address){
|
||||||
|
const self = this
|
||||||
|
self._currentState.selectedAddress = address
|
||||||
|
self._didUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){
|
||||||
|
var self = this
|
||||||
|
|
||||||
|
var time = (new Date()).getTime()
|
||||||
|
var txId = createId()
|
||||||
|
self._currentState.unconfTxs[txId] = {
|
||||||
|
id: txId,
|
||||||
|
txParams: txParams,
|
||||||
|
time: time,
|
||||||
|
status: 'unconfirmed',
|
||||||
|
}
|
||||||
|
console.log('addUnconfirmedTransaction:', txParams)
|
||||||
|
|
||||||
|
// temp - just sign the tx
|
||||||
|
// otherwise we need to keep the cb around
|
||||||
|
// signTransaction(txId, cb)
|
||||||
|
self._unconfTxCbs[txId] = cb
|
||||||
|
|
||||||
|
// signal update
|
||||||
|
self._didUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
IdentityStore.prototype.setLocked = function(){
|
||||||
|
const self = this
|
||||||
|
delete self._keyStore
|
||||||
|
delete window.sessionStorage['password']
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype.submitPassword = function(password, cb){
|
||||||
|
const self = this
|
||||||
|
console.log('submitPassword:', password)
|
||||||
|
self._tryPassword(password, function(err){
|
||||||
|
if (err) console.log('bad password:', password, err)
|
||||||
|
if (err) return cb(err)
|
||||||
|
console.log('good password:', password)
|
||||||
|
window.sessionStorage['password'] = password
|
||||||
|
// load identities before returning...
|
||||||
|
self._loadIdentities()
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype.signTransaction = function(password, txId, cb){
|
||||||
|
const self = this
|
||||||
|
|
||||||
|
var txData = self._currentState.unconfTxs[txId]
|
||||||
|
var txParams = txData.txParams
|
||||||
|
|
||||||
|
self._signTransaction(txParams, function(err, rawTx, txHash){
|
||||||
|
if (err) {
|
||||||
|
throw err
|
||||||
|
txData.status = 'error'
|
||||||
|
txData.error = err
|
||||||
|
self._didUpdate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txData.hash = txHash
|
||||||
|
txData.status = 'pending'
|
||||||
|
|
||||||
|
// for now just remove it
|
||||||
|
delete self._currentState.unconfTxs[txData.id]
|
||||||
|
|
||||||
|
// rpc callback
|
||||||
|
var txSigCb = self._unconfTxCbs[txId] || noop
|
||||||
|
txSigCb(null, rawTx)
|
||||||
|
|
||||||
|
// confirm tx callback
|
||||||
|
cb()
|
||||||
|
|
||||||
|
self._didUpdate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// private
|
||||||
|
//
|
||||||
|
|
||||||
|
// internal - actually signs the tx
|
||||||
|
IdentityStore.prototype._signTransaction = function(txParams, cb){
|
||||||
|
const self = this
|
||||||
|
try {
|
||||||
|
// console.log('signing tx:', txParams)
|
||||||
|
var tx = new Transaction({
|
||||||
|
nonce: txParams.nonce,
|
||||||
|
to: txParams.to,
|
||||||
|
value: txParams.value,
|
||||||
|
data: txParams.input,
|
||||||
|
gasPrice: txParams.gasPrice,
|
||||||
|
gasLimit: txParams.gas,
|
||||||
|
})
|
||||||
|
|
||||||
|
var password = self._getPassword()
|
||||||
|
var serializedTx = self._keyStore.signTx(tx.serialize(), password, self._currentState.selectedAddress)
|
||||||
|
|
||||||
|
// // deserialize and dump values to confirm configuration
|
||||||
|
// var verifyTx = new Transaction(tx.serialize())
|
||||||
|
// console.log('signed transaction:', {
|
||||||
|
// to: '0x'+verifyTx.to.toString('hex'),
|
||||||
|
// from: '0x'+verifyTx.from.toString('hex'),
|
||||||
|
// nonce: '0x'+verifyTx.nonce.toString('hex'),
|
||||||
|
// value: (ethUtil.bufferToInt(verifyTx.value)/1e18)+' ether',
|
||||||
|
// data: '0x'+verifyTx.data.toString('hex'),
|
||||||
|
// gasPrice: '0x'+verifyTx.gasPrice.toString('hex'),
|
||||||
|
// gasLimit: '0x'+verifyTx.gasLimit.toString('hex'),
|
||||||
|
// })
|
||||||
|
cb(null, serializedTx, tx.hash())
|
||||||
|
} catch (err) {
|
||||||
|
cb(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype._didUpdate = function(){
|
||||||
|
const self = this
|
||||||
|
self.emit('update', self.getState())
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype._isUnlocked = function(){
|
||||||
|
const self = this
|
||||||
|
// var password = window.sessionStorage['password']
|
||||||
|
// var result = Boolean(password)
|
||||||
|
var result = Boolean(self._keyStore)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// load identities from keyStore
|
||||||
|
IdentityStore.prototype._loadIdentities = function(){
|
||||||
|
const self = this
|
||||||
|
if (!self._isUnlocked()) throw new Error('not unlocked')
|
||||||
|
// get addresses and normalize address hexString
|
||||||
|
var addresses = self._keyStore.getAddresses().map(function(address){ return '0x'+address })
|
||||||
|
addresses.forEach(function(address){
|
||||||
|
// add to ethStore
|
||||||
|
self._ethStore.addAccount(address)
|
||||||
|
// add to identities
|
||||||
|
var identity = {
|
||||||
|
name: 'Wally',
|
||||||
|
img: 'QmW6hcwYzXrNkuHrpvo58YeZvbZxUddv69ATSHY3BHpPdd',
|
||||||
|
address: address,
|
||||||
|
}
|
||||||
|
self._currentState.identities[address] = identity
|
||||||
|
})
|
||||||
|
self._didUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// keyStore managment - unlocking + deserialization
|
||||||
|
//
|
||||||
|
|
||||||
|
IdentityStore.prototype._tryPassword = function(password, cb){
|
||||||
|
const self = this
|
||||||
|
var keyStore = self._getKeyStore(password)
|
||||||
|
var address = keyStore.getAddresses()[0]
|
||||||
|
if (!address) return cb(new Error('KeyStore - No address to check.'))
|
||||||
|
var hdPathString = keyStore.defaultHdPathString
|
||||||
|
try {
|
||||||
|
var encKey = keyStore.generateEncKey(password)
|
||||||
|
var encPrivKey = keyStore.ksData[hdPathString].encPrivKeys[address]
|
||||||
|
var privKey = KeyStore._decryptKey(encPrivKey, encKey)
|
||||||
|
var addrFromPrivKey = KeyStore._computeAddressFromPrivKey(privKey)
|
||||||
|
} catch (err) {
|
||||||
|
return cb(err)
|
||||||
|
}
|
||||||
|
if (addrFromPrivKey !== address) return cb(new Error('KeyStore - Decrypting private key failed!'))
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype._getKeyStore = function(password){
|
||||||
|
const self = this
|
||||||
|
var keyStore = null
|
||||||
|
var serializedKeystore = window.localStorage['lightwallet']
|
||||||
|
// returning user
|
||||||
|
if (serializedKeystore) {
|
||||||
|
keyStore = KeyStore.deserialize(serializedKeystore)
|
||||||
|
// first time here
|
||||||
|
} else {
|
||||||
|
console.log('creating new keystore with password:', password)
|
||||||
|
var secretSeed = KeyStore.generateRandomSeed()
|
||||||
|
keyStore = new KeyStore(secretSeed, password)
|
||||||
|
keyStore.generateNewAddress(password, 3)
|
||||||
|
self._saveKeystore(keyStore)
|
||||||
|
}
|
||||||
|
keyStore.passwordProvider = function getPassword(cb){
|
||||||
|
cb(null, self._getPassword())
|
||||||
|
}
|
||||||
|
self._keyStore = keyStore
|
||||||
|
return keyStore
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype._saveKeystore = function(keyStore){
|
||||||
|
const self = this
|
||||||
|
window.localStorage['lightwallet'] = keyStore.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityStore.prototype._getPassword = function(){
|
||||||
|
const self = this
|
||||||
|
var password = window.sessionStorage['password']
|
||||||
|
console.warn('using password from memory:', password)
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
|
// util
|
||||||
|
|
||||||
|
function noop(){}
|
@ -1,318 +0,0 @@
|
|||||||
const inherits = require('util').inherits
|
|
||||||
const EventEmitter = require('events').EventEmitter
|
|
||||||
const async = require('async')
|
|
||||||
const KeyStore = require('eth-lightwallet').keystore
|
|
||||||
const createPayload = require('web3-provider-engine/util/create-payload')
|
|
||||||
const createId = require('web3-provider-engine/util/random-id')
|
|
||||||
const Transaction = require('ethereumjs-tx')
|
|
||||||
|
|
||||||
module.exports = IdentityManager
|
|
||||||
|
|
||||||
|
|
||||||
var selectedAddress = null
|
|
||||||
var identities = {}
|
|
||||||
var unconfTxs = {}
|
|
||||||
|
|
||||||
// not part of serilized metamask state - only keep in memory
|
|
||||||
var unconfTxCbs = {}
|
|
||||||
|
|
||||||
var provider = null
|
|
||||||
var defaultPassword = 'test'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inherits(IdentityManager, EventEmitter)
|
|
||||||
function IdentityManager(opts){
|
|
||||||
const self = this
|
|
||||||
self.on('block', function(){
|
|
||||||
self.updateIdentities()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// plugin popup
|
|
||||||
IdentityManager.prototype.getState = getState
|
|
||||||
IdentityManager.prototype.submitPassword = submitPassword
|
|
||||||
IdentityManager.prototype.setSelectedAddress = setSelectedAddress
|
|
||||||
IdentityManager.prototype.signTransaction = signTransaction
|
|
||||||
IdentityManager.prototype.setLocked = setLocked
|
|
||||||
// eth rpc
|
|
||||||
IdentityManager.prototype.getAccounts = getAccounts
|
|
||||||
IdentityManager.prototype.addUnconfirmedTransaction = addUnconfirmedTransaction
|
|
||||||
// etc
|
|
||||||
IdentityManager.prototype.newBlock = newBlock
|
|
||||||
IdentityManager.prototype.setProvider = setProvider
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function setProvider(_provider){
|
|
||||||
provider = _provider
|
|
||||||
}
|
|
||||||
|
|
||||||
function newBlock(block){
|
|
||||||
var self = this
|
|
||||||
self.emit('block', block)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getState(cb){
|
|
||||||
var result = _getState()
|
|
||||||
cb(null, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getState(cb){
|
|
||||||
var unlocked = isUnlocked()
|
|
||||||
var result = {
|
|
||||||
isUnlocked: unlocked,
|
|
||||||
identities: unlocked ? getIdentities() : {},
|
|
||||||
unconfTxs: unlocked ? unconfTxs : {},
|
|
||||||
selectedAddress: selectedAddress,
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUnlocked(){
|
|
||||||
var password = window.sessionStorage['password']
|
|
||||||
var result = Boolean(password)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLocked(){
|
|
||||||
delete window.sessionStorage['password']
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSelectedAddress(address, cb){
|
|
||||||
selectedAddress = address
|
|
||||||
cb(null, _getState())
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitPassword(password, cb){
|
|
||||||
const self = this
|
|
||||||
console.log('submitPassword:', password)
|
|
||||||
tryPassword(password, function(err){
|
|
||||||
if (err) console.log('bad password:', password, err)
|
|
||||||
if (err) return cb(err)
|
|
||||||
console.log('good password:', password)
|
|
||||||
window.sessionStorage['password'] = password
|
|
||||||
// load identities before returning...
|
|
||||||
self.loadIdentities()
|
|
||||||
var state = _getState()
|
|
||||||
cb(null, state)
|
|
||||||
// trigger an update but dont wait for it
|
|
||||||
self.updateIdentities()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the current selected address
|
|
||||||
function getAccounts(cb){
|
|
||||||
var result = selectedAddress ? [selectedAddress] : []
|
|
||||||
cb(null, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIdentities(){
|
|
||||||
return identities
|
|
||||||
}
|
|
||||||
|
|
||||||
// load identities from keyStore
|
|
||||||
IdentityManager.prototype.loadIdentities = function(){
|
|
||||||
const self = this
|
|
||||||
if (!isUnlocked()) throw new Error('not unlocked')
|
|
||||||
var keyStore = getKeyStore()
|
|
||||||
var addresses = keyStore.getAddresses().map(function(address){ return '0x'+address })
|
|
||||||
addresses.forEach(function(address){
|
|
||||||
var identity = {
|
|
||||||
name: 'Wally',
|
|
||||||
img: 'QmW6hcwYzXrNkuHrpvo58YeZvbZxUddv69ATSHY3BHpPdd',
|
|
||||||
address: address,
|
|
||||||
balance: null,
|
|
||||||
txCount: null,
|
|
||||||
}
|
|
||||||
identities[address] = identity
|
|
||||||
})
|
|
||||||
self._didUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityManager.prototype._didUpdate = function(){
|
|
||||||
const self = this
|
|
||||||
self.emit('update', _getState())
|
|
||||||
}
|
|
||||||
|
|
||||||
// foreach in identities, update balance + nonce
|
|
||||||
IdentityManager.prototype.updateIdentities = function(cb){
|
|
||||||
var self = this
|
|
||||||
cb = cb || function(){}
|
|
||||||
if (!isUnlocked()) return cb(new Error('Not unlocked.'))
|
|
||||||
var addresses = Object.keys(identities)
|
|
||||||
async.map(addresses, self.updateIdentity.bind(self), cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets latest info from the network for the identity
|
|
||||||
IdentityManager.prototype.updateIdentity = function(address, cb){
|
|
||||||
var self = this
|
|
||||||
async.parallel([
|
|
||||||
getAccountBalance.bind(null, address),
|
|
||||||
getTxCount.bind(null, address),
|
|
||||||
], function(err, result){
|
|
||||||
if (err) return cb(err)
|
|
||||||
var identity = identities[address]
|
|
||||||
identity.balance = result[0]
|
|
||||||
identity.txCount = result[1]
|
|
||||||
self._didUpdate()
|
|
||||||
cb()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTxCount(address, cb){
|
|
||||||
provider.sendAsync(createPayload({
|
|
||||||
method: 'eth_getTransactionCount',
|
|
||||||
params: [address, 'pending'],
|
|
||||||
}), function(err, res){
|
|
||||||
if (err) return cb(err)
|
|
||||||
if (res.error) return cb(res.error)
|
|
||||||
cb(null, res.result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAccountBalance(address, cb){
|
|
||||||
provider.sendAsync(createPayload({
|
|
||||||
method: 'eth_getBalance',
|
|
||||||
params: [address, 'latest'],
|
|
||||||
}), function(err, res){
|
|
||||||
if (err) return cb(err)
|
|
||||||
if (res.error) return cb(res.error)
|
|
||||||
cb(null, res.result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryPassword(password, cb){
|
|
||||||
var keyStore = getKeyStore(password)
|
|
||||||
var address = keyStore.getAddresses()[0]
|
|
||||||
if (!address) return cb(new Error('KeyStore - No address to check.'))
|
|
||||||
var hdPathString = keyStore.defaultHdPathString
|
|
||||||
try {
|
|
||||||
var encKey = keyStore.generateEncKey(password)
|
|
||||||
var encPrivKey = keyStore.ksData[hdPathString].encPrivKeys[address]
|
|
||||||
var privKey = KeyStore._decryptKey(encPrivKey, encKey)
|
|
||||||
var addrFromPrivKey = KeyStore._computeAddressFromPrivKey(privKey)
|
|
||||||
} catch (err) {
|
|
||||||
return cb(err)
|
|
||||||
}
|
|
||||||
if (addrFromPrivKey !== address) return cb(new Error('KeyStore - Decrypting private key failed!'))
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
|
|
||||||
function addUnconfirmedTransaction(txParams, cb){
|
|
||||||
var self = this
|
|
||||||
|
|
||||||
var time = (new Date()).getTime()
|
|
||||||
var txId = createId()
|
|
||||||
unconfTxs[txId] = {
|
|
||||||
id: txId,
|
|
||||||
txParams: txParams,
|
|
||||||
time: time,
|
|
||||||
status: 'unconfirmed',
|
|
||||||
}
|
|
||||||
console.log('addUnconfirmedTransaction:', txParams)
|
|
||||||
|
|
||||||
// temp - just sign the tx
|
|
||||||
// otherwise we need to keep the cb around
|
|
||||||
// signTransaction(txId, cb)
|
|
||||||
unconfTxCbs[txId] = cb
|
|
||||||
|
|
||||||
// signal update
|
|
||||||
self._didUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// called from
|
|
||||||
function signTransaction(password, txId, cb){
|
|
||||||
const self = this
|
|
||||||
|
|
||||||
var txData = unconfTxs[txId]
|
|
||||||
var txParams = txData.txParams
|
|
||||||
console.log('signTransaction:', txData)
|
|
||||||
|
|
||||||
self._signTransaction(txParams, function(err, rawTx, txHash){
|
|
||||||
if (err) {
|
|
||||||
txData.status = 'error'
|
|
||||||
txData.error = err
|
|
||||||
self._didUpdate()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
txData.hash = txHash
|
|
||||||
txData.status = 'pending'
|
|
||||||
// for now just kill it
|
|
||||||
delete unconfTxs[txData.id]
|
|
||||||
|
|
||||||
var txSigCb = unconfTxCbs[txId] || function(){}
|
|
||||||
txSigCb(null, rawTx)
|
|
||||||
|
|
||||||
cb(null, _getState())
|
|
||||||
self._didUpdate()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal - actually signs the tx
|
|
||||||
IdentityManager.prototype._signTransaction = function(txParams, cb){
|
|
||||||
try {
|
|
||||||
// console.log('signing tx:', txParams)
|
|
||||||
var tx = new Transaction({
|
|
||||||
nonce: txParams.nonce,
|
|
||||||
to: txParams.to,
|
|
||||||
value: txParams.value,
|
|
||||||
data: txParams.input,
|
|
||||||
gasPrice: txParams.gasPrice,
|
|
||||||
gasLimit: txParams.gas,
|
|
||||||
})
|
|
||||||
|
|
||||||
var keyStore = getKeyStore()
|
|
||||||
var serializedTx = keyStore.signTx(tx.serialize(), defaultPassword, selectedAddress)
|
|
||||||
|
|
||||||
// // deserialize and dump values to confirm configuration
|
|
||||||
// var verifyTx = new Transaction(tx.serialize())
|
|
||||||
// console.log('signed transaction:', {
|
|
||||||
// to: '0x'+verifyTx.to.toString('hex'),
|
|
||||||
// from: '0x'+verifyTx.from.toString('hex'),
|
|
||||||
// nonce: '0x'+verifyTx.nonce.toString('hex'),
|
|
||||||
// value: (ethUtil.bufferToInt(verifyTx.value)/1e18)+' ether',
|
|
||||||
// data: '0x'+verifyTx.data.toString('hex'),
|
|
||||||
// gasPrice: '0x'+verifyTx.gasPrice.toString('hex'),
|
|
||||||
// gasLimit: '0x'+verifyTx.gasLimit.toString('hex'),
|
|
||||||
// })
|
|
||||||
cb(null, serializedTx, tx.hash())
|
|
||||||
} catch (err) {
|
|
||||||
cb(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyStore = null
|
|
||||||
function getKeyStore(password){
|
|
||||||
if (keyStore) return keyStore
|
|
||||||
password = password || getPassword()
|
|
||||||
var serializedKeystore = window.localStorage['lightwallet']
|
|
||||||
// returning user
|
|
||||||
if (serializedKeystore) {
|
|
||||||
keyStore = KeyStore.deserialize(serializedKeystore)
|
|
||||||
// first time here
|
|
||||||
} else {
|
|
||||||
console.log('creating new keystore with default password:', defaultPassword)
|
|
||||||
var secretSeed = KeyStore.generateRandomSeed()
|
|
||||||
keyStore = new KeyStore(secretSeed, defaultPassword)
|
|
||||||
keyStore.generateNewAddress(defaultPassword, 3)
|
|
||||||
saveKeystore()
|
|
||||||
}
|
|
||||||
keyStore.passwordProvider = unlockKeystore
|
|
||||||
return keyStore
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveKeystore(){
|
|
||||||
window.localStorage['lightwallet'] = keyStore.serialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPassword(){
|
|
||||||
var password = window.sessionStorage['password']
|
|
||||||
if (!password) throw new Error('No password found...')
|
|
||||||
}
|
|
||||||
|
|
||||||
function unlockKeystore(cb){
|
|
||||||
var password = getPassword()
|
|
||||||
console.warn('unlocking keystore...')
|
|
||||||
cb(null, password)
|
|
||||||
}
|
|
@ -9,6 +9,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^1.5.2",
|
"async": "^1.5.2",
|
||||||
|
"clone": "^1.0.2",
|
||||||
"dnode": "^1.2.2",
|
"dnode": "^1.2.2",
|
||||||
"end-of-stream": "^1.1.0",
|
"end-of-stream": "^1.1.0",
|
||||||
"eth-lightwallet": "^1.0.1",
|
"eth-lightwallet": "^1.0.1",
|
||||||
|
Loading…
Reference in New Issue
Block a user