1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 11:22:43 +02:00

Merge pull request #81 from MetaMask/MigratableConfig

Migratable config
This commit is contained in:
Dan Finlay 2016-03-31 11:02:23 -07:00
commit f451da67a7
9 changed files with 270 additions and 48 deletions

3
.gitignore vendored
View File

@ -8,4 +8,5 @@ app/bower_components
test/bower_components
package
.DS_Store
.DS_Store
builds/

View File

@ -1,6 +1,6 @@
{
"name": "__MSG_appName__",
"version": "1.2.0",
"version": "1.2.1",
"manifest_version": 2,
"description": "__MSG_appDescription__",
"icons": {

View File

@ -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(){

View File

@ -0,0 +1,3 @@
var ConfigManager = require('./config-manager')
module.exports = new ConfigManager()

View File

@ -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
}

View File

@ -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
}

View File

@ -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",

View File

@ -1,4 +1,2 @@
require('mocha-sinon')()
var jsdom = require('mocha-jsdom')
jsdom()
require('jsdom-global')()
window.localStorage = {}

View File

@ -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)
})
})
})