aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Finlay <somniac@me.com>2016-04-01 02:02:23 +0800
committerDan Finlay <somniac@me.com>2016-04-01 02:02:23 +0800
commitf451da67a725636c5b504737a8bc45774a6fc5c3 (patch)
tree1346ea1582acb5ceb0156b023637f3288a525185
parent18b75e679f148f973346bd12d90f2bb0203769c0 (diff)
parentf5105293bf2d3e6fb325e015b5b2b67baa7d03cc (diff)
downloadtangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.tar
tangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.tar.gz
tangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.tar.bz2
tangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.tar.lz
tangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.tar.xz
tangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.tar.zst
tangerine-wallet-browser-f451da67a725636c5b504737a8bc45774a6fc5c3.zip
Merge pull request #81 from MetaMask/MigratableConfig
Migratable config
-rw-r--r--.gitignore3
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/background.js22
-rw-r--r--app/scripts/lib/config-manager-singleton.js3
-rw-r--r--app/scripts/lib/config-manager.js157
-rw-r--r--app/scripts/lib/idStore.js48
-rw-r--r--package.json6
-rw-r--r--test/helper.js6
-rw-r--r--test/unit/config-manager-test.js71
9 files changed, 270 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore
index 0ef0ceb2e..476b197db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,5 @@ app/bower_components
test/bower_components
package
-.DS_Store \ No newline at end of file
+.DS_Store
+builds/
diff --git a/app/manifest.json b/app/manifest.json
index 6faedf6d4..915295914 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,6 +1,6 @@
{
"name": "__MSG_appName__",
- "version": "1.2.0",
+ "version": "1.2.1",
"manifest_version": 2,
"description": "__MSG_appDescription__",
"icons": {
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 1a0d36ef8..772c1de89 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -9,8 +9,7 @@ const PortStream = require('./lib/port-stream.js')
const MetaMaskProvider = require('web3-provider-engine/zero.js')
const IdentityStore = require('./lib/idStore')
const createTxNotification = require('./lib/tx-notification.js')
-
-console.log('ready to roll')
+const configManager = require('./lib/config-manager-singleton')
//
// connect to other contexts
@@ -37,10 +36,9 @@ function handleEthRpcRequestStream(stream){
// state and network
//
-var config = getConfig()
var idStore = new IdentityStore()
var zeroClient = MetaMaskProvider({
- rpcUrl: config.rpcTarget,
+ rpcUrl: configManager.getCurrentRpcAddress(),
getAccounts: function(cb){
var selectedAddress = idStore.getSelectedAddress()
var result = selectedAddress ? [selectedAddress] : []
@@ -62,7 +60,7 @@ function getState(){
var state = extend(
ethStore.getState(),
idStore.getState(),
- getConfig()
+ configManager.getConfig()
)
return state
}
@@ -177,22 +175,10 @@ function addUnconfirmedTx(txParams, cb){
// called from popup
function setRpcTarget(rpcTarget){
- var config = getConfig()
- config.rpcTarget = rpcTarget
- setConfig(config)
+ configManager.setRpcTarget(rpcTarget)
chrome.runtime.reload()
}
-function getConfig(){
- return extend({
- rpcTarget: 'https://rawtestrpc.metamask.io/',
- }, JSON.parse(localStorage['config'] || '{}'))
-}
-
-function setConfig(state){
- localStorage['config'] = JSON.stringify(state)
-}
-
// util
function jsonParseStream(){
diff --git a/app/scripts/lib/config-manager-singleton.js b/app/scripts/lib/config-manager-singleton.js
new file mode 100644
index 000000000..5915c401b
--- /dev/null
+++ b/app/scripts/lib/config-manager-singleton.js
@@ -0,0 +1,3 @@
+var ConfigManager = require('./config-manager')
+
+module.exports = new ConfigManager()
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
new file mode 100644
index 000000000..682b34637
--- /dev/null
+++ b/app/scripts/lib/config-manager.js
@@ -0,0 +1,157 @@
+const Migrator = require('pojo-migrator')
+const extend = require('xtend')
+
+const STORAGE_KEY = 'metamask-config'
+var DEFAULT_RPC = 'https://rawtestrpc.metamask.io/'
+
+/* The config-manager is a convenience object
+ * wrapping a pojo-migrator.
+ *
+ * It exists mostly to allow the creation of
+ * convenience methods to access and persist
+ * particular portions of the state.
+ */
+module.exports = ConfigManager
+function ConfigManager() {
+
+ /* The migrator exported on the config-manager
+ * has two methods the user should be concerned with:
+ *
+ * getData(), which returns the app-consumable data object
+ * saveData(), which persists the app-consumable data object.
+ */
+ this.migrator = new Migrator({
+
+ // Migrations must start at version 1 or later.
+ // They are objects with a `version` number
+ // and a `migrate` function.
+ //
+ // The `migrate` function receives the previous
+ // config data format, and returns the new one.
+ migrations: [],
+
+ // How to load initial config.
+ // Includes step on migrating pre-pojo-migrator data.
+ loadData: loadData,
+
+ // How to persist migrated config.
+ setData: function(data) {
+ window.localStorage[STORAGE_KEY] = JSON.stringify(data)
+ },
+ })
+}
+
+ConfigManager.prototype.setConfig = function(config) {
+ var data = this.migrator.getData()
+ data.config = config
+ this.setData(data)
+}
+
+ConfigManager.prototype.setRpcTarget = function(rpcUrl) {
+ var config = this.getConfig()
+ config.provider = {
+ type: 'rpc',
+ rpcTarget: rpcUrl,
+ }
+ this.setConfig(config)
+}
+
+ConfigManager.prototype.getConfig = function() {
+ var data = this.migrator.getData()
+ if ('config' in data) {
+ return data.config
+ } else {
+ return {
+ provider: {
+ type: 'rpc',
+ rpcTarget: DEFAULT_RPC,
+ }
+ }
+ }
+}
+
+ConfigManager.prototype.setData = function(data) {
+ this.migrator.saveData(data)
+}
+
+ConfigManager.prototype.getData = function() {
+ return this.migrator.getData()
+}
+
+ConfigManager.prototype.setWallet = function(wallet) {
+ var data = this.migrator.getData()
+ data.wallet = wallet
+ this.setData(data)
+}
+
+ConfigManager.prototype.getWallet = function() {
+ return this.migrator.getData().wallet
+}
+
+// Takes a boolean
+ConfigManager.prototype.setShowSeedWords = function(should) {
+ var data = this.migrator.getData()
+ data.showSeedWords = should
+ this.setData(data)
+}
+
+ConfigManager.prototype.getShouldShowSeedWords = function() {
+ var data = this.migrator.getData()
+ return data.showSeedWords
+}
+
+ConfigManager.prototype.getCurrentRpcAddress = function() {
+ var config = this.getConfig()
+ if (!config) return null
+ return config.provider && config.provider.rpcTarget ? config.provider.rpcTarget : DEFAULT_RPC
+}
+
+ConfigManager.prototype.clearWallet = function() {
+ var data = this.getConfig()
+ delete data.wallet
+ this.setData(data)
+}
+
+function loadData() {
+
+ var oldData = getOldStyleData()
+ var newData
+ try {
+ newData = JSON.parse(window.localStorage[STORAGE_KEY])
+ } catch (e) {}
+
+ var data = extend({
+ version: 0,
+ data: {
+ config: {
+ rpcTarget: DEFAULT_RPC,
+ }
+ }
+ }, oldData ? oldData : null, newData ? newData : null)
+ return data
+}
+
+function getOldStyleData() {
+ var config, wallet, seedWords
+
+ var result = {
+ meta: { version: 0 },
+ data: {},
+ }
+
+ try {
+ config = JSON.parse(window.localStorage['config'])
+ result.data.config = config
+ } catch (e) {}
+ try {
+ wallet = JSON.parse(window.localStorage['lightwallet'])
+ result.data.wallet = wallet
+ } catch (e) {}
+ try {
+ seedWords = window.localStorage['seedWords']
+ result.data.seedWords = seedWords
+ } catch (e) {}
+
+ return result
+}
+
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index ea873e627..1bc1ebcb2 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -8,6 +8,7 @@ const clone = require('clone')
const extend = require('xtend')
const createId = require('web3-provider-engine/util/random-id')
const autoFaucet = require('./auto-faucet')
+const configManager = require('./config-manager-singleton')
module.exports = IdentityStore
@@ -41,14 +42,16 @@ function IdentityStore(ethStore) {
IdentityStore.prototype.createNewVault = function(password, entropy, cb){
delete this._keyStore
- delete window.localStorage['lightwallet']
+ configManager.clearWallet()
this._createIdmgmt(password, null, entropy, (err) => {
if (err) return cb(err)
- var seedWords = this._idmgmt.getSeed()
- this._cacheSeedWordsUntilConfirmed(seedWords)
+
this._loadIdentities()
this._didUpdate()
this._autoFaucet()
+
+ configManager.setShowSeedWords(true)
+ var seedWords = this._idmgmt.getSeed()
cb(null, seedWords)
})
}
@@ -68,19 +71,28 @@ IdentityStore.prototype.setStore = function(store){
}
IdentityStore.prototype.clearSeedWordCache = function(cb) {
- delete window.localStorage['seedWords']
+ configManager.setShowSeedWords(false)
cb()
}
IdentityStore.prototype.getState = function(){
- const cachedSeeds = window.localStorage['seedWords']
+ var seedWords = this.getSeedIfUnlocked()
+ var wallet = configManager.getWallet()
return clone(extend(this._currentState, {
- isInitialized: !!window.localStorage['lightwallet'] && !cachedSeeds,
+ isInitialized: !!configManager.getWallet() && !seedWords,
isUnlocked: this._isUnlocked(),
- seedWords: cachedSeeds,
+ seedWords: seedWords,
}))
}
+IdentityStore.prototype.getSeedIfUnlocked = function() {
+ var showSeed = configManager.getShouldShowSeedWords()
+ var idmgmt = this._idmgmt
+ var shouldShow = showSeed && !!idmgmt
+ var seedWords = shouldShow ? idmgmt.getSeed() : null
+ return seedWords
+}
+
IdentityStore.prototype.getSelectedAddress = function(){
return this._currentState.selectedAddress
}
@@ -181,10 +193,6 @@ IdentityStore.prototype._isUnlocked = function(){
return result
}
-IdentityStore.prototype._cacheSeedWordsUntilConfirmed = function(seedWords) {
- window.localStorage['seedWords'] = seedWords
-}
-
// load identities from keyStoreet
IdentityStore.prototype._loadIdentities = function(){
if (!this._isUnlocked()) throw new Error('not unlocked')
@@ -216,14 +224,14 @@ IdentityStore.prototype._createIdmgmt = function(password, seed, entropy, cb){
var keyStore = null
LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
if (err) return cb(err)
- var serializedKeystore = window.localStorage['lightwallet']
+ var serializedKeystore = configManager.getWallet()
if (seed) {
keyStore = this._restoreFromSeed(password, seed, derivedKey)
- // returning user, recovering from localStorage
+ // returning user, recovering from storage
} else if (serializedKeystore) {
- keyStore = this._loadFromLocalStorage(serializedKeystore, derivedKey, cb)
+ keyStore = LightwalletKeyStore.deserialize(serializedKeystore)
var isCorrect = keyStore.isDerivedKeyCorrect(derivedKey)
if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
@@ -249,15 +257,11 @@ IdentityStore.prototype._restoreFromSeed = function(password, seed, derivedKey)
keyStore.setDefaultHdDerivationPath(this.hdPathString)
keyStore.generateNewAddress(derivedKey, 3)
- window.localStorage['lightwallet'] = keyStore.serialize()
- console.log('restored from seed. saved to keystore localStorage')
+ configManager.setWallet(keyStore.serialize())
+ console.log('restored from seed. saved to keystore')
return keyStore
}
-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, this.hdPathString)
@@ -265,8 +269,8 @@ IdentityStore.prototype._createFirstWallet = function(entropy, derivedKey) {
keyStore.setDefaultHdDerivationPath(this.hdPathString)
keyStore.generateNewAddress(derivedKey, 3)
- window.localStorage['lightwallet'] = keyStore.serialize()
- console.log('saved to keystore localStorage')
+ configManager.setWallet(keyStore.serialize())
+ console.log('saved to keystore')
return keyStore
}
diff --git a/package.json b/package.json
index f482fd4f6..be61f1d14 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,8 @@
"private": true,
"scripts": {
"start": "gulp dev",
- "test": "mocha --compilers js:babel-register --recursive"
+ "test": "mocha --require test/helper.js --compilers js:babel-register --recursive",
+ "watch": "mocha watch --compilers js:babel-register --recursive"
},
"dependencies": {
"async": "^1.5.2",
@@ -21,6 +22,7 @@
"inject-css": "^0.1.1",
"metamask-ui": "^1.5.0",
"multiplex": "^6.7.0",
+ "pojo-migrator": "^2.1.0",
"pumpify": "^1.3.4",
"readable-stream": "^2.0.5",
"through2": "^2.0.1",
@@ -39,10 +41,10 @@
"gulp-util": "^3.0.7",
"gulp-watch": "^4.3.5",
"jsdom": "^8.1.0",
+ "jsdom-global": "^1.7.0",
"jshint-stylish": "~0.1.5",
"lodash.assign": "^4.0.6",
"mocha": "^2.4.5",
- "mocha-jsdom": "^1.1.0",
"mocha-sinon": "^1.1.5",
"sinon": "^1.17.3",
"vinyl-buffer": "^1.0.0",
diff --git a/test/helper.js b/test/helper.js
index 7746b90e0..4c7f8b4c6 100644
--- a/test/helper.js
+++ b/test/helper.js
@@ -1,4 +1,2 @@
-require('mocha-sinon')()
-var jsdom = require('mocha-jsdom')
-jsdom()
-
+require('jsdom-global')()
+window.localStorage = {}
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
new file mode 100644
index 000000000..10b6716d6
--- /dev/null
+++ b/test/unit/config-manager-test.js
@@ -0,0 +1,71 @@
+var assert = require('assert')
+var ConfigManager = require('../../app/scripts/lib/config-manager')
+var configManager
+
+describe('config-manager', function() {
+
+ before(function() {
+ window.localStorage = {} // Hacking localStorage support into JSDom
+ configManager = new ConfigManager()
+ })
+
+ describe('#setConfig', function() {
+ window.localStorage = {} // Hacking localStorage support into JSDom
+
+ it('should set the config key', function () {
+ var testConfig = {
+ provider: {
+ type: 'rpc',
+ rpcTarget: 'foobar'
+ }
+ }
+ configManager.setConfig(testConfig)
+ var result = configManager.getData()
+
+ assert.equal(result.config.provider.type, testConfig.provider.type)
+ assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
+ })
+
+ it('setting wallet should not overwrite config', function() {
+ var testConfig = {
+ provider: {
+ type: 'rpc',
+ rpcTarget: 'foobar'
+ }
+ }
+ configManager.setConfig(testConfig)
+
+ var testWallet = {
+ name: 'this is my fake wallet'
+ }
+ configManager.setWallet(testWallet)
+
+ var result = configManager.getData()
+ assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
+ assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
+
+ testConfig.provider.type = 'something else!'
+ configManager.setConfig(testConfig)
+
+ result = configManager.getData()
+ assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
+ assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
+ assert.equal(result.config.provider.type, testConfig.provider.type)
+ })
+ })
+
+ describe('rpc manipulations', function() {
+ it('changing rpc should return a different rpc', function() {
+ var firstRpc = 'first'
+ var secondRpc = 'second'
+
+ configManager.setRpcTarget(firstRpc)
+ var firstResult = configManager.getCurrentRpcAddress()
+ assert.equal(firstResult, firstRpc)
+
+ configManager.setRpcTarget(secondRpc)
+ var secondResult = configManager.getCurrentRpcAddress()
+ assert.equal(secondResult, secondRpc)
+ })
+ })
+})