From a2c7ccafa6b4cdbd5f1725f7bf40189ad987cffa Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 25 Mar 2016 14:51:19 -0700 Subject: [PATCH] Implemented BIP44 compliance test. Also added the hdPath that Christian had told me to our calls to the LightWallet, but this does not seem to have made us generate the same accounts as `testrpc` yet. --- app/scripts/lib/idStore.js | 100 ++++++++++-------------- package.json | 6 +- test/helper.js | 4 + test/{index.js => unit/idStore-test.js} | 37 ++++++++- 4 files changed, 84 insertions(+), 63 deletions(-) create mode 100644 test/helper.js rename test/{index.js => unit/idStore-test.js} (53%) diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index 1a3e7e06f..1491effcc 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -53,18 +53,17 @@ IdentityStore.prototype.createNewVault = function(password, entropy, cb){ } IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){ - const self = this - self._createIdmgmt(password, seed, null, function(err){ + this._createIdmgmt(password, seed, null, (err) => { if (err) return cb(err) - self._loadIdentities() - self._didUpdate() + + this._loadIdentities() + this._didUpdate() cb() }) } IdentityStore.prototype.setStore = function(store){ - const self = this - self._ethStore = store + this._ethStore = store } IdentityStore.prototype.clearSeedWordCache = function(cb) { @@ -73,46 +72,40 @@ IdentityStore.prototype.clearSeedWordCache = function(cb) { } IdentityStore.prototype.getState = function(){ - const self = this const cachedSeeds = window.localStorage['seedWords'] - return clone(extend(self._currentState, { + return clone(extend(this._currentState, { isInitialized: !!window.localStorage['lightwallet'] && !cachedSeeds, - isUnlocked: self._isUnlocked(), + isUnlocked: this._isUnlocked(), seedWords: cachedSeeds, })) } IdentityStore.prototype.getSelectedAddress = function(){ - const self = this - return self._currentState.selectedAddress + return this._currentState.selectedAddress } IdentityStore.prototype.setSelectedAddress = function(address){ - const self = this - self._currentState.selectedAddress = address - self._didUpdate() + this._currentState.selectedAddress = address + this._didUpdate() } IdentityStore.prototype.setLocked = function(cb){ - const self = this - delete self._keyStore - delete self._idmgmt + delete this._keyStore + delete this._idmgmt cb() } IdentityStore.prototype.submitPassword = function(password, cb){ - const self = this - self._tryPassword(password, function(err){ + this._tryPassword(password, (err) => { if (err) return cb(err) // load identities before returning... - self._loadIdentities() + this._loadIdentities() cb() }) } // comes from dapp via zero-client hooked-wallet provider IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){ - var self = this // create txData obj with parameters and meta data var time = (new Date()).getTime() @@ -123,55 +116,51 @@ IdentityStore.prototype.addUnconfirmedTransaction = function(txParams, cb){ time: time, status: 'unconfirmed', } - self._currentState.unconfTxs[txId] = txData + this._currentState.unconfTxs[txId] = txData + console.log('addUnconfirmedTransaction:', txData) // keep the cb around for after approval (requires user interaction) - self._unconfTxCbs[txId] = cb + this._unconfTxCbs[txId] = cb // signal update - self._didUpdate() + this._didUpdate() return txId } // comes from metamask ui IdentityStore.prototype.approveTransaction = function(txId, cb){ - const self = this - - var txData = self._currentState.unconfTxs[txId] + var txData = this._currentState.unconfTxs[txId] var txParams = txData.txParams - var approvalCb = self._unconfTxCbs[txId] || noop + var approvalCb = this._unconfTxCbs[txId] || noop // accept tx cb() approvalCb(null, true) // clean up - delete self._currentState.unconfTxs[txId] - delete self._unconfTxCbs[txId] - self._didUpdate() + delete this._currentState.unconfTxs[txId] + delete this._unconfTxCbs[txId] + this._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 + var txData = this._currentState.unconfTxs[txId] + var approvalCb = this._unconfTxCbs[txId] || noop // reject tx approvalCb(null, false) // clean up - delete self._currentState.unconfTxs[txId] - delete self._unconfTxCbs[txId] - self._didUpdate() + delete this._currentState.unconfTxs[txId] + delete this._unconfTxCbs[txId] + this._didUpdate() } // performs the actual signing, no autofill of params IdentityStore.prototype.signTransaction = function(txParams, cb){ - const self = this try { console.log('signing tx...', txParams) - var rawTx = self._idmgmt.signTx(txParams) + var rawTx = this._idmgmt.signTx(txParams) cb(null, rawTx) } catch (err) { cb(err) @@ -183,13 +172,11 @@ IdentityStore.prototype.signTransaction = function(txParams, cb){ // IdentityStore.prototype._didUpdate = function(){ - const self = this - self.emit('update', self.getState()) + this.emit('update', this.getState()) } IdentityStore.prototype._isUnlocked = function(){ - const self = this - var result = Boolean(self._keyStore) && Boolean(self._idmgmt) + var result = Boolean(this._keyStore) && Boolean(this._idmgmt) return result } @@ -199,22 +186,21 @@ IdentityStore.prototype._cacheSeedWordsUntilConfirmed = function(seedWords) { // load identities from keyStoreet IdentityStore.prototype._loadIdentities = function(){ - const self = this - if (!self._isUnlocked()) throw new Error('not unlocked') + if (!this._isUnlocked()) throw new Error('not unlocked') // get addresses and normalize address hexString - var addresses = self._keyStore.getAddresses(this.hdPathString).map(function(address){ return '0x'+address }) - addresses.forEach(function(address){ + var addresses = this._keyStore.getAddresses(this.hdPathString).map((address) => { return '0x'+address }) + addresses.forEach((address) => { // // add to ethStore - self._ethStore.addAccount(address) + this._ethStore.addAccount(address) // add to identities var identity = { name: 'Wally', img: 'QmW6hcwYzXrNkuHrpvo58YeZvbZxUddv69ATSHY3BHpPdd', address: address, } - self._currentState.identities[address] = identity + this._currentState.identities[address] = identity }) - self._didUpdate() + this._didUpdate() } // @@ -222,8 +208,7 @@ IdentityStore.prototype._loadIdentities = function(){ // IdentityStore.prototype._tryPassword = function(password, cb){ - const self = this - self._createIdmgmt(password, null, null, cb) + this._createIdmgmt(password, null, null, cb) } IdentityStore.prototype._createIdmgmt = function(password, seed, entropy, cb){ @@ -233,7 +218,7 @@ IdentityStore.prototype._createIdmgmt = function(password, seed, entropy, cb){ var serializedKeystore = window.localStorage['lightwallet'] if (seed) { - this._restoreFromSeed(keyStore, seed, derivedKey) + keyStore = this._restoreFromSeed(seed, derivedKey) // returning user, recovering from localStorage } else if (serializedKeystore) { @@ -256,11 +241,12 @@ IdentityStore.prototype._createIdmgmt = function(password, seed, entropy, cb){ }) } -IdentityStore.prototype._restoreFromSeed = function(keyStore, seed, derivedKey) { - keyStore = new LightwalletKeyStore(seed, derivedKey) - keyStore.generateNewAddress(derivedKey, 3, hdPathString) +IdentityStore.prototype._restoreFromSeed = function(seed, derivedKey) { + var keyStore = new LightwalletKeyStore(seed, derivedKey, this.hdPathString) + keyStore.generateNewAddress(derivedKey, 3, this.hdPathString) window.localStorage['lightwallet'] = keyStore.serialize() console.log('restored from seed. saved to keystore localStorage') + return keyStore } IdentityStore.prototype._loadFromLocalStorage = function(serializedKeystore, derivedKey) { diff --git a/package.json b/package.json index da7686c0a..e31f9e09a 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "private": true, "scripts": { "start": "gulp dev", - "test": "mocha --compilers js:babel-register", - "watch": "mocha watch --compilers js:babel-register" + "test": "mocha --compilers js:babel-register --recursive", + "watch": "mocha watch --compilers js:babel-register --recursive" }, "dependencies": { "async": "^1.5.2", @@ -44,6 +44,8 @@ "lodash.assign": "^4.0.6", "mocha": "^2.4.5", "mocha-jsdom": "^1.1.0", + "mocha-sinon": "^1.1.5", + "sinon": "^1.17.3", "tape": "^4.5.1", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", diff --git a/test/helper.js b/test/helper.js new file mode 100644 index 000000000..7746b90e0 --- /dev/null +++ b/test/helper.js @@ -0,0 +1,4 @@ +require('mocha-sinon')() +var jsdom = require('mocha-jsdom') +jsdom() + diff --git a/test/index.js b/test/unit/idStore-test.js similarity index 53% rename from test/index.js rename to test/unit/idStore-test.js index 5a8df78b8..4494bf505 100644 --- a/test/index.js +++ b/test/unit/idStore-test.js @@ -1,7 +1,5 @@ var assert = require('assert') -var IdentityStore = require('../app/scripts/lib/idStore') -var jsdom = require('mocha-jsdom') -jsdom() +var IdentityStore = require('../../app/scripts/lib/idStore') describe('IdentityStore', function() { @@ -38,15 +36,46 @@ describe('IdentityStore', function() { }) }) - it('should return the expected keystore', function () { + it('should return the expected keystore', function (done) { idStore.recoverFromSeed(password, seedWords, (err) => { assert.ifError(err) let newKeystore = idStore._idmgmt.keyStore assert.equal(newKeystore, originalKeystore) + done() }) }) }) }) + + describe('#recoverFromSeed BIP44 compliance', function() { + let seedWords = 'picnic injury awful upper eagle junk alert toss flower renew silly vague' + let firstAccount = '0xaceef0221414801dde7f732196b1c9d8ea60b637' + let password = 'secret!' + let accounts = [] + let idStore + + before(function() { + window.localStorage = {} // Hacking localStorage support into JSDom + + idStore = new IdentityStore({ + addAccount(acct) { + console.log(`pushing account ${acct}`) + accounts.push(acct) + }, + }) + }) + + it('should return the expected first account', function (done) { + + idStore.recoverFromSeed(password, seedWords, (err) => { + assert.ifError(err) + + let newKeystore = idStore._idmgmt.keyStore + assert.equal(accounts[0], firstAccount) + done() + }) + }) + }) })