diff options
-rw-r--r-- | app/scripts/background.js | 5 | ||||
-rw-r--r-- | app/scripts/lib/idStore.js | 231 |
2 files changed, 95 insertions, 141 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js index 17238f227..fb2fad54b 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -62,7 +62,7 @@ function onRpcRequest(remotePort, payload){ // console.log('MetaMaskPlugin - incoming payload:', payload) zeroClient.sendAsync(payload, function onPayloadHandled(err, response){ if (err) throw err - // console.log('MetaMaskPlugin - RPC complete:', payload, '->', response) + console.log('MetaMaskPlugin - RPC complete:', payload, '->', response) try { remotePort.postMessage(response) } catch (_) { @@ -84,9 +84,8 @@ function handleInternalCommunication(remotePort){ createNewVault: idStore.createNewVault.bind(idStore), submitPassword: idStore.submitPassword.bind(idStore), setSelectedAddress: idStore.setSelectedAddress.bind(idStore), - signTransaction: idStore.signTransaction.bind(idStore), + approveTransaction: idStore.approveTransaction.bind(idStore), cancelTransaction: idStore.cancelTransaction.bind(idStore), - sendTransaction: idStore.sendTransaction.bind(idStore), setLocked: idStore.setLocked.bind(idStore), }) duplex.pipe(connection).pipe(duplex) diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index d26a7391c..366372442 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -1,7 +1,8 @@ const EventEmitter = require('events').EventEmitter const inherits = require('util').inherits const Transaction = require('ethereumjs-tx') -const KeyStore = require('eth-lightwallet').keystore +const LightwalletKeyStore = require('eth-lightwallet').keystore +const LightwalletSigner = require('eth-lightwallet').signing const async = require('async') const clone = require('clone') const extend = require('xtend') @@ -18,6 +19,10 @@ function IdentityStore(ethStore) { // we just use the ethStore to auto-add accounts self._ethStore = ethStore + // lightwallet key store + self._keyStore = null + // lightwallet wrapper + self._idmgmt = null self._currentState = { selectedAddress: null, @@ -36,11 +41,13 @@ IdentityStore.prototype.createNewVault = function(password, cb){ const self = this delete self._keyStore delete window.localStorage['lightwallet'] - var keyStore = self._getKeyStore(password) - var seedWords = keyStore.getSeed(password) - self._loadIdentities() - self._didUpdate() - cb(null, seedWords) + var keyStore = self._createIdmgmt(password, function(err){ + if (err) return cb(err) + var seedWords = self._idmgmt.getSeed() + self._loadIdentities() + self._didUpdate() + cb(null, seedWords) + }) } @@ -69,141 +76,93 @@ IdentityStore.prototype.setSelectedAddress = function(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 self._idmgmt } 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) // 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 +// comes from dapp via zero-client hooked-wallet provider +IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){ + var self = this - self._signTransaction(password, txParams, function(err, rawTx, txHash){ - if (err) { - cb(err) - txData.status = 'error' - txData.error = err - self._didUpdate() - return - } + // create txData obj with parameters and meta data + var time = (new Date()).getTime() + var txId = createId() + var txData = { + id: txId, + txParams: txParams, + time: time, + status: 'unconfirmed', + } + self._currentState.unconfTxs[txId] = txData + console.log('addUnconfirmedTransaction:', txData) - txData.rawTx = rawTx - txData.hash = txHash - txData.status = 'signed' + // keep the cb around for after approval (requires user interaction) + self._unconfTxCbs[txId] = cb - // confirm tx signed - cb() - self._didUpdate() - }) + // signal update + self._didUpdate() } -IdentityStore.prototype.sendTransaction = function(txId, cb){ +// comes from metamask ui +IdentityStore.prototype.approveTransaction = function(txId, cb){ const self = this var txData = self._currentState.unconfTxs[txId] + var txParams = txData.txParams + var approvalCb = self._unconfTxCbs[txId] || noop - if (!txData || txData.status !== 'signed') { - return cb(new Error('IdentityStore - Transaction not signed:', txId)) - } - - var rawTx = txData.rawTx - - // for now just remove it - delete self._currentState.unconfTxs[txData.id] - - // rpc callback - var txSigCb = self._unconfTxCbs[txId] || noop - txSigCb(null, rawTx) - - // confirm tx sent + // accept tx cb() + approvalCb(null, true) + // clean up + delete self._currentState.unconfTxs[txId] + delete self._unconfTxCbs[txId] self._didUpdate() } +// comes from metamask ui IdentityStore.prototype.cancelTransaction = function(txId){ const self = this var txData = self._currentState.unconfTxs[txId] + var approvalCb = self._unconfTxCbs[txId] || noop + + // reject tx + approvalCb(null, false) + // clean up delete self._currentState.unconfTxs[txId] + delete self._unconfTxCbs[txId] self._didUpdate() } -// -// private -// - -// internal - actually signs the tx -IdentityStore.prototype._signTransaction = function(password, txParams, cb){ +// performs the actual signing, no autofill of params +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.data, - gasPrice: txParams.gasPrice, - gasLimit: txParams.gas, - }) - - 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()) + console.log('signing tx...', txParams) + var rawTx = self._idmgmt.signTx(txParams) + cb(null, rawTx) } catch (err) { cb(err) } } +// +// private +// + IdentityStore.prototype._didUpdate = function(){ const self = this self.emit('update', self.getState()) @@ -211,8 +170,7 @@ IdentityStore.prototype._didUpdate = function(){ IdentityStore.prototype._isUnlocked = function(){ const self = this - // var result = Boolean(password) - var result = Boolean(self._keyStore) + var result = Boolean(self._keyStore) && Boolean(self._idmgmt) return result } @@ -242,48 +200,45 @@ IdentityStore.prototype._loadIdentities = function(){ 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() + self._createIdmgmt(password, cb) } -IdentityStore.prototype._getKeyStore = function(password){ +IdentityStore.prototype._createIdmgmt = function(password, cb){ 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, password) - } - self._keyStore = keyStore - return keyStore -} - -IdentityStore.prototype._saveKeystore = function(keyStore){ - const self = this - window.localStorage['lightwallet'] = keyStore.serialize() - console.log('saved to localStorage') + LightwalletKeyStore.deriveKeyFromPassword(password, function(err, derrivedKey){ + if (err) return cb(err) + var serializedKeystore = window.localStorage['lightwallet'] + // returning user + if (serializedKeystore) { + keyStore = LightwalletKeyStore.deserialize(serializedKeystore) + var isCorrect = keyStore.isDerivedKeyCorrect(derrivedKey) + if (!isCorrect) return cb(new Error('Lightwallet - password incorrect')) + // first time here + } else { + var secretSeed = LightwalletKeyStore.generateRandomSeed() + keyStore = new LightwalletKeyStore(secretSeed, derrivedKey) + keyStore.generateNewAddress(derrivedKey, 3) + window.localStorage['lightwallet'] = keyStore.serialize() + console.log('saved to keystore localStorage') + } + self._keyStore = keyStore + self._idmgmt = { + getAddresses: function(){ + return keyStore.getAddresses().map(function(address){ return '0x'+address }) + }, + signTx: function(txParams){ + txParams.gasLimit = txParams.gas + var tx = new Transaction(txParams) + var rawTx = '0x'+tx.serialize().toString('hex') + return '0x'+LightwalletSigner.signTx(keyStore, derrivedKey, rawTx, txParams.from) + }, + getSeed: function(){ + return keyStore.getSeed(derrivedKey) + }, + } + cb() + }) } // util |