Add seed word caching during confirmation screen

In order to persist the seed word page until the user clicks the confirmation button, we need to store the seed words in localStorage.

To simplify this process I've also reorganized some of the account manager code, broken up one large function into many smaller functions, and created a new class for the IdMgmt object.

Again, sorry such a big refactor in one commit, but I really had to break it down to work through it.
This commit is contained in:
Dan Finlay 2016-03-24 10:32:50 -07:00
parent 84789d674a
commit 55f8ae4edd
3 changed files with 88 additions and 49 deletions

1
app/images/loading.svg Normal file
View File

@ -0,0 +1 @@
<svg width='120px' height='120px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-default"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(0 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(40 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.1111111111111111s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(80 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.2222222222222222s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(120 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.3333333333333333s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(160 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.4444444444444444s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(200 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.5555555555555556s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(240 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.6666666666666666s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(280 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.7777777777777778s' repeatCount='indefinite'/></rect><rect x='46' y='39' width='8' height='22' rx='5' ry='5' fill='#ffae29' transform='rotate(320 50 50) translate(0 -20)'> <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.8888888888888888s' repeatCount='indefinite'/></rect></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -121,6 +121,7 @@ function linkDnode(stream){
approveTransaction: idStore.approveTransaction.bind(idStore), approveTransaction: idStore.approveTransaction.bind(idStore),
cancelTransaction: idStore.cancelTransaction.bind(idStore), cancelTransaction: idStore.cancelTransaction.bind(idStore),
setLocked: idStore.setLocked.bind(idStore), setLocked: idStore.setLocked.bind(idStore),
clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore),
}) })
stream.pipe(connection).pipe(stream) stream.pipe(connection).pipe(stream)
connection.on('remote', function(remote){ connection.on('remote', function(remote){

View File

@ -37,22 +37,22 @@ function IdentityStore(ethStore) {
// public // public
// //
IdentityStore.prototype.createNewVault = function(password, cb){ IdentityStore.prototype.createNewVault = function(password, entropy, cb){
const self = this delete this._keyStore
delete self._keyStore
delete window.localStorage['lightwallet'] delete window.localStorage['lightwallet']
var keyStore = self._createIdmgmt(password, null, function(err){ this._createIdmgmt(password, null, entropy, (err) => {
if (err) return cb(err) if (err) return cb(err)
var seedWords = self._idmgmt.getSeed() var seedWords = this._idmgmt.getSeed()
self._loadIdentities() this._cacheSeedWordsUntilConfirmed(seedWords)
self._didUpdate() this._loadIdentities()
this._didUpdate()
cb(null, seedWords) cb(null, seedWords)
}) })
} }
IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){ IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){
const self = this const self = this
self._createIdmgmt(password, seed, function(err){ self._createIdmgmt(password, seed, null, function(err){
if (err) return cb(err) if (err) return cb(err)
self._loadIdentities() self._loadIdentities()
self._didUpdate() self._didUpdate()
@ -65,12 +65,18 @@ IdentityStore.prototype.setStore = function(store){
self._ethStore = store self._ethStore = store
} }
IdentityStore.prototype.clearSeedWordCache = function(cb) {
delete window.localStorage['seedWords']
cb()
}
IdentityStore.prototype.getState = function(){ IdentityStore.prototype.getState = function(){
const self = this const self = this
const cachedSeeds = window.localStorage['seedWords']
return clone(extend(self._currentState, { return clone(extend(self._currentState, {
isInitialized: !!window.localStorage['lightwallet'], isInitialized: !!window.localStorage['lightwallet'] && !cachedSeeds,
isUnlocked: self._isUnlocked(), isUnlocked: self._isUnlocked(),
seedWords: cachedSeeds,
})) }))
} }
@ -185,6 +191,10 @@ IdentityStore.prototype._isUnlocked = function(){
return result return result
} }
IdentityStore.prototype._cacheSeedWordsUntilConfirmed = function(seedWords) {
window.localStorage['seedWords'] = seedWords
}
// load identities from keyStoreet // load identities from keyStoreet
IdentityStore.prototype._loadIdentities = function(){ IdentityStore.prototype._loadIdentities = function(){
const self = this const self = this
@ -211,40 +221,68 @@ IdentityStore.prototype._loadIdentities = function(){
IdentityStore.prototype._tryPassword = function(password, cb){ IdentityStore.prototype._tryPassword = function(password, cb){
const self = this const self = this
self._createIdmgmt(password, null, cb) self._createIdmgmt(password, null, null, cb)
} }
IdentityStore.prototype._createIdmgmt = function(password, seed, cb){ IdentityStore.prototype._createIdmgmt = function(password, seed, entropy, cb){
const self = this
var keyStore = null var keyStore = null
LightwalletKeyStore.deriveKeyFromPassword(password, function(err, derrivedKey){ LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
if (err) return cb(err) if (err) return cb(err)
var serializedKeystore = window.localStorage['lightwallet'] var serializedKeystore = window.localStorage['lightwallet']
// recovering from seed
if (seed) { if (seed) {
keyStore = new LightwalletKeyStore(seed, derrivedKey) this._restoreFromSeed(keyStore, seed, derivedKey)
keyStore.generateNewAddress(derrivedKey, 3)
window.localStorage['lightwallet'] = keyStore.serialize()
console.log('saved to keystore localStorage')
// returning user, recovering from localStorage // returning user, recovering from localStorage
} else if (serializedKeystore) { } else if (serializedKeystore) {
keyStore = LightwalletKeyStore.deserialize(serializedKeystore) keyStore = this._loadFromLocalStorage(serializedKeystore, derivedKey, cb)
var isCorrect = keyStore.isDerivedKeyCorrect(derrivedKey) var isCorrect = keyStore.isDerivedKeyCorrect(derivedKey)
if (!isCorrect) return cb(new Error('Lightwallet - password incorrect')) if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
// first time here // first time here
} else { } else {
var secretSeed = LightwalletKeyStore.generateRandomSeed() keyStore = this._createFirstWallet(entropy, derivedKey)
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 = { this._keyStore = keyStore
getAddresses: function(){ this._idmgmt = new IdManagement({
keyStore: keyStore,
derivedKey: derivedKey,
})
cb()
})
}
IdentityStore.prototype._restoreFromSeed = function(keyStore, seed, derivedKey) {
keyStore = new LightwalletKeyStore(seed, derivedKey)
keyStore.generateNewAddress(derivedKey, 3)
window.localStorage['lightwallet'] = keyStore.serialize()
console.log('restored from seed. saved to keystore localStorage')
}
IdentityStore.prototype._loadFromLocalStorage = function(serializedKeystore, derivedKey) {
return LightwalletKeyStore.deserialize(serializedKeystore)
}
IdentityStore.prototype._createFirstWallet = function(entropy, derivedKey) {
var secretSeed = LightwalletKeyStore.generateRandomSeed(entropy)
var keyStore = new LightwalletKeyStore(secretSeed, derivedKey)
keyStore.generateNewAddress(derivedKey, 3)
window.localStorage['lightwallet'] = keyStore.serialize()
console.log('wallet generated. saved to keystore localStorage')
return keyStore
}
function IdManagement( opts = { keyStore: null, derivedKey: null } ) {
this.keyStore = opts.keyStore
this.derivedKey = opts.derivedKey
this.getAddresses = function(){
return keyStore.getAddresses().map(function(address){ return '0x'+address }) return keyStore.getAddresses().map(function(address){ return '0x'+address })
}, }
signTx: function(txParams){
this.signTx = function(txParams){
// normalize values // normalize values
txParams.to = ethUtil.addHexPrefix(txParams.to) txParams.to = ethUtil.addHexPrefix(txParams.to)
txParams.from = ethUtil.addHexPrefix(txParams.from) txParams.from = ethUtil.addHexPrefix(txParams.from)
@ -254,16 +292,15 @@ IdentityStore.prototype._createIdmgmt = function(password, seed, cb){
txParams.nonce = ethUtil.addHexPrefix(txParams.nonce) txParams.nonce = ethUtil.addHexPrefix(txParams.nonce)
var tx = new Transaction(txParams) var tx = new Transaction(txParams)
var rawTx = '0x'+tx.serialize().toString('hex') var rawTx = '0x'+tx.serialize().toString('hex')
return '0x'+LightwalletSigner.signTx(keyStore, derrivedKey, rawTx, txParams.from) return '0x'+LightwalletSigner.signTx(this.keyStore, this.derivedKey, rawTx, txParams.from)
}, }
getSeed: function(){
return keyStore.getSeed(derrivedKey) this.getSeed = function(){
}, return this.keyStore.getSeed(this.derivedKey)
} }
cb()
})
} }
// util // util
function noop(){} function noop(){}