From 722acdad35e229c80ebb59a8d6672c430016a7b2 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 16 Jan 2016 16:22:54 -0800 Subject: [PATCH] breakout idmgmt --- app/scripts/background.js | 234 ++------------------------- app/scripts/lib/idmgmt.js | 233 ++++++++++++++++++++++++++ app/scripts/lib/metamask-provider.js | 10 +- 3 files changed, 251 insertions(+), 226 deletions(-) create mode 100644 app/scripts/lib/idmgmt.js diff --git a/app/scripts/background.js b/app/scripts/background.js index 48f14172e..ca6b5abbc 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,17 +1,25 @@ const Dnode = require('dnode') -const KeyStore = require('eth-lightwallet').keystore const PortStream = require('./lib/port-stream.js') const MetaMaskProvider = require('./lib/metamask-provider') +const IdentityManager = require('./lib/idmgmt') console.log('ready to roll') +var wallet = IdentityManager() + // setup provider var zeroClient = MetaMaskProvider({ - rpcUrl: 'https://testrpc.metamask.io/', - getAccounts: getAccounts, - sendTransaction: confirmTransaction, + rpcUrl: 'https://rawtestrpc.metamask.io/', + getAccounts: wallet.getAccounts.bind(wallet), + sendTransaction: wallet.confirmTransaction.bind(wallet), }) +wallet.setProvider(zeroClient) +zeroClient.on('block', function(block){ + wallet.newBlock(block) +}) + + // setup messaging chrome.runtime.onConnect.addListener(connectRemote) function connectRemote(remotePort){ @@ -27,13 +35,7 @@ function connectRemote(remotePort){ function handleInternalCommunication(remotePort){ var duplex = new PortStream(remotePort) - var remote = Dnode({ - getState: getState, - setLocked: setLocked, - submitPassword: submitPassword, - setSelectedAddress: setSelectedAddress, - signTransaction: signTransaction, - }) + var remote = Dnode(wallet) duplex.pipe(remote).pipe(duplex) } @@ -51,159 +53,6 @@ function onRpcRequest(remotePort, payload){ }) } -// id mgmt -var selectedAddress = null - -function getState(cb){ - var result = _getState() - cb(null, result) -} - -function _getState(cb){ - var unlocked = isUnlocked() - var result = { - isUnlocked: unlocked, - identities: unlocked ? getIdentities() : {}, - 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){ - 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 - cb(null, _getState()) - }) -} - -function getAccounts(cb){ - var identities = getIdentities() - var result = selectedAddress ? [selectedAddress] : [] - cb(null, result) -} - -function getIdentities(cb){ - var keyStore = getKeyStore() - var addresses = keyStore.getAddresses() - var accountStore = {} - addresses.map(function(address){ - address = '0x'+address - accountStore[address] = { - name: 'Wally', - img: 'QmW6hcwYzXrNkuHrpvo58YeZvbZxUddv69ATSHY3BHpPdd', - address: address, - balance: 10.005, - txCount: 16, - } - }) - return accountStore -} - -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 confirmTransaction(txParams, cb){ - console.log('confirmTransaction:', txParams) -} - -function signTransaction(txParams, cb){ - console.log('signTransaction:', txParams) -} - -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 { - var defaultPassword = 'test' - 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) -} - -// // load from storage -// chrome.storage.sync.get(function(data){ -// for (var key in data) { -// var serialized = data[key] -// var tx = deserializeTx(serialized) -// var hash = simpleHash(serialized) -// unsignedTxs[hash] = tx -// } -// updateBadge() -// }) - -// // listen to storage changes -// chrome.storage.onChanged.addListener(function(changes, namespace) { -// for (key in changes) { -// var storageChange = changes[key] -// if (storageChange.oldValue && !storageChange.newValue) { -// // was removed -// removeTransaction(storageChange.oldValue) -// } else if (!storageChange.oldValue && storageChange.newValue) { -// // was added -// addTransaction(deserializeTx(storageChange.newValue)) -// } -// } -// }) - // setup badge text // updateBadge() @@ -231,60 +80,3 @@ function unlockKeystore(cb){ // } // } - -// function addTransaction(tx){ -// var serialized = serializeTx(tx) -// var hash = simpleHash(serialized) -// unsignedTxs[hash] = tx -// var data = {} -// data[hash] = serialized -// chrome.storage.sync.set(data) -// // trigger ui changes -// updateBadge() -// } - -// function removeTransaction(serialized){ -// var hash = simpleHash(serialized) -// delete unsignedTxs[hash] -// var data = {} -// data[hash] = undefined -// chrome.storage.sync.set(data) -// // trigger ui changes -// updateBadge() -// } - -// function exportUnsignedTxs(remote){ -// console.log('exporting txs!', unsignedTxs) -// var data = { -// type: 'importUnsignedTxs', -// payload: getValues(unsignedTxs), -// } -// remote.postMessage(data) -// } - -// function simpleHash(input) { -// var hash = 0, i, chr, len -// if (input.length == 0) return hash -// for (i = 0, len = input.length; i < len; i++) { -// chr = input.charCodeAt(i) -// hash = ((hash << 5) - hash) + chr -// hash |= 0 // Convert to 32bit integer -// } -// return hash -// } - -// function serializeTx(tx){ -// return JSON.stringify(tx) -// } - -// function deserializeTx(tx){ -// return JSON.parse(tx) -// } - -// function getValues(obj){ -// var output = [] -// for (var key in obj) { -// output.push(obj[key]) -// } -// return output -// } \ No newline at end of file diff --git a/app/scripts/lib/idmgmt.js b/app/scripts/lib/idmgmt.js new file mode 100644 index 000000000..b13116a45 --- /dev/null +++ b/app/scripts/lib/idmgmt.js @@ -0,0 +1,233 @@ +const EventEmitter = require('events').EventEmitter +const async = require('async') +const KeyStore = require('eth-lightwallet').keystore +const createPayload = require('web3-provider-engine/util/create-payload') +var selectedAddress = null +var identities = {} + +module.exports = IdentityManager + + +var provider = null +var pubsub = new EventEmitter() + +function IdentityManager(opts){ + opts = opts || {} + providerEngine = opts.providerEngine + + return { + // plugin popup + getState: getState, + subscribe: subscribe, + submitPassword: submitPassword, + setSelectedAddress: setSelectedAddress, + signTransaction: signTransaction, + setLocked: setLocked, + // eth rpc + getAccounts: getAccounts, + confirmTransaction: confirmTransaction, + // etc + newBlock: newBlock, + setProvider: setProvider, + } +} + +function setProvider(_provider){ + provider = _provider +} + +function newBlock(block){ + pubsub.emit('block', block) + updateIdentities() +} + +// on new block, update our accounts (but only if we're unlocked) +function subscribe(cb){ + pubsub.on('block', sendUpdateState) + function sendUpdateState(){ + if (!isUnlocked()) return + updateIdentities(function(){ + var state = _getState() + cb(state) + }) + } +} + +function getState(cb){ + var result = _getState() + cb(null, result) +} + +function _getState(cb){ + var unlocked = isUnlocked() + var result = { + isUnlocked: unlocked, + identities: unlocked ? getIdentities() : {}, + 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){ + 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... + loadIdentities() + var state = _getState() + cb(null, state) + // trigger an update but dont wait for it + updateIdentities() + }) +} + +// get the current selected address +function getAccounts(cb){ + var result = selectedAddress ? [selectedAddress] : [] + console.log('getAccounts:', result) + cb(null, result) +} + +function getIdentities(){ + return identities +} + +// load identities from keyStore +function loadIdentities(){ + 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 + }) +} + +// foreach in identities, update balance + nonce +function updateIdentities(cb){ + cb = cb || function(){} + if (!isUnlocked()) return cb(new Error('Not unlocked.')) + var addresses = Object.keys(identities) + async.map(addresses, updateIdentity, cb) +} + +// gets latest info from the network for the identity +function updateIdentity(address, cb){ + 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] + cb() + }) +} + +function getTxCount(address, cb){ + provider.sendAsync(createPayload({ + method: 'eth_getTransactionCount', + params: [address], + }), 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], + }), 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 confirmTransaction(txParams, cb){ + console.log('confirmTransaction:', txParams) +} + +function signTransaction(txParams, cb){ + console.log('signTransaction:', txParams) +} + +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 { + var defaultPassword = 'test' + 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) +} \ No newline at end of file diff --git a/app/scripts/lib/metamask-provider.js b/app/scripts/lib/metamask-provider.js index d7d06d3f1..09326e3c1 100644 --- a/app/scripts/lib/metamask-provider.js +++ b/app/scripts/lib/metamask-provider.js @@ -42,11 +42,11 @@ function metamaskProvider(opts){ })) // log new blocks - // engine.on('block', function(block){ - // console.log('================================') - // console.log('BLOCK CHANGED:', '#'+block.number.toString('hex'), '0x'+block.hash.toString('hex')) - // console.log('================================') - // }) + engine.on('block', function(block){ + // console.log('================================') + console.log('BLOCK CHANGED:', '#'+block.number.toString('hex'), '0x'+block.hash.toString('hex')) + // console.log('================================') + }) // start polling for blocks engine.start()