aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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.js161
-rw-r--r--app/scripts/lib/etherscan-provider.js62
-rw-r--r--app/scripts/lib/idStore.js38
-rw-r--r--package.json6
-rw-r--r--test/helper.js6
-rw-r--r--test/unit/config-manager-test.js71
8 files changed, 266 insertions, 103 deletions
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..038774a30
--- /dev/null
+++ b/app/scripts/lib/config-manager.js
@@ -0,0 +1,161 @@
+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
+}
+
+ConfigManager.prototype.getSeedWords = function() {
+ return this.migrator.getData().seedWords
+}
+
+ConfigManager.prototype.setSeedWords = function(seedWords) {
+ var data = this.migrator.getData()
+ data.seedWords = seedWords
+ this.setData(data)
+}
+
+ConfigManager.prototype.clearSeedWords = function() {
+ var data = this.migrator.getData()
+ delete data.seedWords
+ this.setData(data)
+}
+
+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/etherscan-provider.js b/app/scripts/lib/etherscan-provider.js
deleted file mode 100644
index a2334c367..000000000
--- a/app/scripts/lib/etherscan-provider.js
+++ /dev/null
@@ -1,62 +0,0 @@
-const xhr = process.browser ? require('xhr') : require('request')
-const inherits = require('util').inherits
-const createPayload = require('../util/create-payload.js')
-
-module.exports = EtherScanProvider
-
-function EtherScanProvider(opts) {
-
- this.url = opts.url || 'http://testnet.etherscan.io/api?module=proxy&'
-
-}
-
-EtherScanProvider.prototype.setEngine = function(engine) {
- const self = this
- self.engine = engine
- engine.on('block', function(block) {
- self.currentBlock = block
- })
-}
-
-EtherScanProvider.prototype.handleRequest = function(payload, next, end) {
-
- const self = this
- var method = payload.method
- var targetUrl = self.rpcUrl + 'action=' + method
- var params = payload.params
-
- var newPayload = createPayload(payload)
-
- xhr({
- uri: targetUrl,
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(newPayload),
- rejectUnauthorized: false,
- }, function(err, res, body) {
- if (err) return end(err)
-
- // parse response into raw account
- var data
- try {
- data = JSON.parse(body)
- if (data.error) return end(data.error)
- } catch (err) {
- console.error(err.stack)
- return end(err)
- }
-
- // console.log('network:', payload.method, payload.params, '->', data.result)
-
- end(null, data.result)
- })
-
-}
-
-EtherScanProvider.prototype.emitPayload = function(payload, cb){
- const self = this
- self.engine.sendAsync(createPayload(payload), cb)
-}
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index ea873e627..425a4348c 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,11 +42,11 @@ 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)
+ configManager.setSeedWords(seedWords)
this._loadIdentities()
this._didUpdate()
this._autoFaucet()
@@ -68,14 +69,17 @@ IdentityStore.prototype.setStore = function(store){
}
IdentityStore.prototype.clearSeedWordCache = function(cb) {
- delete window.localStorage['seedWords']
+ configManager.clearSeedWords()
cb()
}
IdentityStore.prototype.getState = function(){
- const cachedSeeds = window.localStorage['seedWords']
+ const cachedSeeds = configManager.getSeedWords()
+ var wallet = configManager.getWallet()
+ console.log('wallet:')
+ console.dir(wallet)
return clone(extend(this._currentState, {
- isInitialized: !!window.localStorage['lightwallet'] && !cachedSeeds,
+ isInitialized: !!configManager.getWallet() && !cachedSeeds,
isUnlocked: this._isUnlocked(),
seedWords: cachedSeeds,
}))
@@ -181,10 +185,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 +216,18 @@ 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()
+ console.log("DESERIALIZED WALLET AND IT LOOKS LIKE THIS")
+ console.dir(serializedKeystore)
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 = this.deserializeKeystore(serializedKeystore)
+ console.log("DESERIALIZED KEYSTORE AND IT LOOKS LIKE THIS")
+ console.dir(keyStore)
var isCorrect = keyStore.isDerivedKeyCorrect(derivedKey)
if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
@@ -249,12 +253,12 @@ 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) {
+IdentityStore.prototype.deserializeKeystore = function(serializedKeystore) {
return LightwalletKeyStore.deserialize(serializedKeystore)
}
@@ -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)
+ })
+ })
+})