Merge pull request #5236 from whymarrh/excise-config-manager

Delete ConfigManager class and replace its valid usages
feature/default_network_editable
Dan Finlay 6 years ago committed by GitHub
commit 1d95dc72b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 18
      app/scripts/controllers/preferences.js
  3. 254
      app/scripts/lib/config-manager.js
  4. 46
      app/scripts/metamask-controller.js
  5. 50
      app/scripts/migrations/_multi-keyring.js
  6. 2
      app/scripts/notice-controller.js
  7. 1
      docs/adding-new-networks.md
  8. 2
      package.json
  9. 9
      test/lib/mock-config-manager.js
  10. 34
      test/unit/app/controllers/metamask-controller-test.js
  11. 7
      test/unit/app/controllers/notice-controller-test.js
  12. 30
      test/unit/app/controllers/preferences-controller-test.js
  13. 112
      test/unit/config-manager-test.js

1
.gitignore vendored

@ -21,6 +21,7 @@ temp
.DS_Store
app/.DS_Store
coverage/
dist
builds/
disc/

@ -36,6 +36,8 @@ class PreferencesController {
currentLocale: opts.initLangCode,
identities: {},
lostIdentities: {},
seedWords: null,
forgottenPassword: false,
}, opts.initState)
this.diagnostics = opts.diagnostics
@ -46,6 +48,22 @@ class PreferencesController {
}
// PUBLIC METHODS
/**
* Sets the {@code forgottenPassword} state property
* @param {boolean} forgottenPassword whether or not the user has forgotten their password
*/
setPasswordForgotten (forgottenPassword) {
this.store.updateState({ forgottenPassword })
}
/**
* Sets the {@code seedWords} seed words
* @param {string|null} seedWords the seed words
*/
setSeedWords (seedWords) {
this.store.updateState({ seedWords })
}
/**
* Setter for the `useBlockie` property
*

@ -1,254 +0,0 @@
const ethUtil = require('ethereumjs-util')
const normalize = require('eth-sig-util').normalize
const {
MAINNET_RPC_URL,
ROPSTEN_RPC_URL,
KOVAN_RPC_URL,
RINKEBY_RPC_URL,
} = require('../controllers/network/enums')
/* 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 (opts) {
// ConfigManager is observable and will emit updates
this._subs = []
this.store = opts.store
}
ConfigManager.prototype.setConfig = function (config) {
var data = this.getData()
data.config = config
this.setData(data)
this._emitUpdates(config)
}
ConfigManager.prototype.getConfig = function () {
var data = this.getData()
return data.config
}
ConfigManager.prototype.setData = function (data) {
this.store.putState(data)
}
ConfigManager.prototype.getData = function () {
return this.store.getState()
}
ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) {
const data = this.getData()
data.forgottenPassword = passwordForgottenState
this.setData(data)
}
ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) {
const data = this.getData()
return data.forgottenPassword
}
ConfigManager.prototype.setWallet = function (wallet) {
var data = this.getData()
data.wallet = wallet
this.setData(data)
}
ConfigManager.prototype.setVault = function (encryptedString) {
var data = this.getData()
data.vault = encryptedString
this.setData(data)
}
ConfigManager.prototype.getVault = function () {
var data = this.getData()
return data.vault
}
ConfigManager.prototype.getKeychains = function () {
return this.getData().keychains || []
}
ConfigManager.prototype.setKeychains = function (keychains) {
var data = this.getData()
data.keychains = keychains
this.setData(data)
}
ConfigManager.prototype.getSelectedAccount = function () {
var config = this.getConfig()
return config.selectedAccount
}
ConfigManager.prototype.setSelectedAccount = function (address) {
var config = this.getConfig()
config.selectedAccount = ethUtil.addHexPrefix(address)
this.setConfig(config)
}
ConfigManager.prototype.getWallet = function () {
return this.getData().wallet
}
// Takes a boolean
ConfigManager.prototype.setShowSeedWords = function (should) {
var data = this.getData()
data.showSeedWords = should
this.setData(data)
}
ConfigManager.prototype.getShouldShowSeedWords = function () {
var data = this.getData()
return data.showSeedWords
}
ConfigManager.prototype.setSeedWords = function (words) {
var data = this.getData()
data.seedWords = words
this.setData(data)
}
ConfigManager.prototype.getSeedWords = function () {
var data = this.getData()
return data.seedWords
}
ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
var config = this.getConfig()
config.provider = {
type: 'rpc',
rpcTarget: rpcUrl,
}
this.setConfig(config)
}
ConfigManager.prototype.setProviderType = function (type) {
var config = this.getConfig()
config.provider = {
type: type,
}
this.setConfig(config)
}
ConfigManager.prototype.useEtherscanProvider = function () {
var config = this.getConfig()
config.provider = {
type: 'etherscan',
}
this.setConfig(config)
}
ConfigManager.prototype.getProvider = function () {
var config = this.getConfig()
return config.provider
}
ConfigManager.prototype.getCurrentRpcAddress = function () {
var provider = this.getProvider()
if (!provider) return null
switch (provider.type) {
case 'mainnet':
return MAINNET_RPC_URL
case 'ropsten':
return ROPSTEN_RPC_URL
case 'kovan':
return KOVAN_RPC_URL
case 'rinkeby':
return RINKEBY_RPC_URL
default:
return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC_URL
}
}
//
// Tx
//
ConfigManager.prototype.getTxList = function () {
var data = this.getData()
if (data.transactions !== undefined) {
return data.transactions
} else {
return []
}
}
ConfigManager.prototype.setTxList = function (txList) {
var data = this.getData()
data.transactions = txList
this.setData(data)
}
// wallet nickname methods
ConfigManager.prototype.getWalletNicknames = function () {
var data = this.getData()
const nicknames = ('walletNicknames' in data) ? data.walletNicknames : {}
return nicknames
}
ConfigManager.prototype.nicknameForWallet = function (account) {
const address = normalize(account)
const nicknames = this.getWalletNicknames()
return nicknames[address]
}
ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
const address = normalize(account)
const nicknames = this.getWalletNicknames()
nicknames[address] = nickname
var data = this.getData()
data.walletNicknames = nicknames
this.setData(data)
}
// observable
ConfigManager.prototype.getSalt = function () {
var data = this.getData()
return data.salt
}
ConfigManager.prototype.setSalt = function (salt) {
var data = this.getData()
data.salt = salt
this.setData(data)
}
ConfigManager.prototype.subscribe = function (fn) {
this._subs.push(fn)
var unsubscribe = this.unsubscribe.bind(this, fn)
return unsubscribe
}
ConfigManager.prototype.unsubscribe = function (fn) {
var index = this._subs.indexOf(fn)
if (index !== -1) this._subs.splice(index, 1)
}
ConfigManager.prototype._emitUpdates = function (state) {
this._subs.forEach(function (handler) {
handler(state)
})
}
ConfigManager.prototype.setLostAccounts = function (lostAccounts) {
var data = this.getData()
data.lostAccounts = lostAccounts
this.setData(data)
}
ConfigManager.prototype.getLostAccounts = function () {
var data = this.getData()
return data.lostAccounts || []
}

@ -36,7 +36,6 @@ const TransactionController = require('./controllers/transactions')
const BalancesController = require('./controllers/computed-balances')
const TokenRatesController = require('./controllers/token-rates')
const DetectTokensController = require('./controllers/detect-tokens')
const ConfigManager = require('./lib/config-manager')
const nodeify = require('./lib/nodeify')
const accountImporter = require('./account-import-strategies')
const getBuyEthUrl = require('./lib/buy-eth-url')
@ -83,11 +82,6 @@ module.exports = class MetamaskController extends EventEmitter {
// network store
this.networkController = new NetworkController(initState.NetworkController)
// config manager
this.configManager = new ConfigManager({
store: this.store,
})
// preferences controller
this.preferencesController = new PreferencesController({
initState: initState.PreferencesController,
@ -314,18 +308,15 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Object} status
*/
getState () {
const wallet = this.configManager.getWallet()
const vault = this.keyringController.store.getState().vault
const isInitialized = (!!wallet || !!vault)
const isInitialized = !!vault
return {
...{ isInitialized },
...this.memStore.getFlatState(),
...this.configManager.getConfig(),
...{
lostAccounts: this.configManager.getLostAccounts(),
seedWords: this.configManager.getSeedWords(),
forgottenPassword: this.configManager.getPasswordForgotten(),
// TODO: Remove usages of lost accounts
lostAccounts: [],
},
}
}
@ -727,7 +718,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.verifySeedPhrase()
.then((seedWords) => {
this.configManager.setSeedWords(seedWords)
this.preferencesController.setSeedWords(seedWords)
return cb(null, seedWords)
})
.catch((err) => {
@ -776,7 +767,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {function} cb Callback function called with the current address.
*/
clearSeedWordCache (cb) {
this.configManager.setSeedWords(null)
this.preferencesController.setSeedWords(null)
cb(null, this.preferencesController.getSelectedAddress())
}
@ -1037,35 +1028,16 @@ module.exports = class MetamaskController extends EventEmitter {
/**
* A legacy method used to record user confirmation that they understand
* that some of their accounts have been recovered but should be backed up.
* This function no longer does anything and will be removed.
*
* @deprecated
* @param {Function} cb - A callback function called with a full state update.
*/
markAccountsFound (cb) {
this.configManager.setLostAccounts([])
this.sendUpdate()
// TODO Remove me
cb(null, this.getState())
}
/**
* A legacy method (probably dead code) that was used when we swapped out our
* key management library that we depended on.
*
* Described in:
* https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd
*
* @deprecated
* @param {} migratorOutput
*/
restoreOldLostAccounts (migratorOutput) {
const { lostAccounts } = migratorOutput
if (lostAccounts) {
this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
return this.importLostAccounts(migratorOutput)
}
return Promise.resolve(migratorOutput)
}
/**
* An account object
* @typedef Account
@ -1144,7 +1116,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} cb - A callback function called when complete.
*/
markPasswordForgotten (cb) {
this.configManager.setPasswordForgotten(true)
this.preferencesController.setPasswordForgotten(true)
this.sendUpdate()
cb()
}
@ -1154,7 +1126,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Function} cb - A callback function called when complete.
*/
unMarkPasswordForgotten (cb) {
this.configManager.setPasswordForgotten(false)
this.preferencesController.setPasswordForgotten(false)
this.sendUpdate()
cb()
}

@ -1,50 +0,0 @@
const version = 5
/*
This is an incomplete migration bc it requires post-decrypted data
which we dont have access to at the time of this writing.
*/
const ObservableStore = require('obs-store')
const ConfigManager = require('../../app/scripts/lib/config-manager')
const IdentityStoreMigrator = require('../../app/scripts/lib/idStore-migrator')
const KeyringController = require('eth-keyring-controller')
const password = 'obviously not correct'
module.exports = {
version,
migrate: function (versionedData) {
versionedData.meta.version = version
const store = new ObservableStore(versionedData.data)
const configManager = new ConfigManager({ store })
const idStoreMigrator = new IdentityStoreMigrator({ configManager })
const keyringController = new KeyringController({
configManager: configManager,
})
// attempt to migrate to multiVault
return idStoreMigrator.migratedVaultForPassword(password)
.then((result) => {
// skip if nothing to migrate
if (!result) return Promise.resolve(versionedData)
delete versionedData.data.wallet
// create new keyrings
const privKeys = result.lostAccounts.map(acct => acct.privateKey)
return Promise.all([
keyringController.restoreKeyring(result.serialized),
keyringController.restoreKeyring({ type: 'Simple Key Pair', data: privKeys }),
]).then(() => {
return keyringController.persistAllKeyrings(password)
}).then(() => {
// copy result on to state object
versionedData.data = store.get()
return Promise.resolve(versionedData)
})
})
},
}

@ -7,7 +7,7 @@ const uniqBy = require('lodash.uniqby')
module.exports = class NoticeController extends EventEmitter {
constructor (opts) {
constructor (opts = {}) {
super()
this.noticePoller = null
this.firstVersion = opts.firstVersion

@ -5,7 +5,6 @@ To add another network to our dropdown menu, make sure the following files are a
```
app/scripts/config.js
app/scripts/lib/buy-eth-url.js
app/scripts/lib/config-manager.js
ui/app/app.js
ui/app/components/buy-button-subview.js
ui/app/components/drop-menu-item.js

@ -22,7 +22,7 @@
"test:e2e:run:firefox": "SELENIUM_BROWSER=firefox mocha test/e2e/metamask.spec --bail --recursive",
"test:screens": "shell-parallel -s 'npm run ganache:start' -x 'sleep 3 && npm run test:screens:run'",
"test:screens:run": "node test/screens/new-ui.js",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
"test:coverage": "nyc --reporter=text --reporter=html npm run test:unit && npm run test:coveralls-upload",
"test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
"test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests && npm run test:flat:build:locales",

@ -1,9 +0,0 @@
const ObservableStore = require('obs-store')
const clone = require('clone')
const ConfigManager = require('../../app/scripts/lib/config-manager')
const firstTimeState = require('../../app/scripts/first-time-state')
module.exports = function () {
const store = new ObservableStore(clone(firstTimeState))
return new ConfigManager({ store })
}

@ -584,22 +584,18 @@ describe('MetaMaskController', function () {
})
describe('#clearSeedWordCache', function () {
it('should set seed words to null', function (done) {
sandbox.stub(metamaskController.preferencesController, 'setSeedWords')
metamaskController.clearSeedWordCache((err) => {
if (err) {
done(err)
}
it('should have set seed words', function () {
metamaskController.configManager.setSeedWords('test words')
const getConfigSeed = metamaskController.configManager.getSeedWords()
assert.equal(getConfigSeed, 'test words')
})
it('should clear config seed phrase', function () {
metamaskController.configManager.setSeedWords('test words')
metamaskController.clearSeedWordCache((err, result) => {
if (err) console.log(err)
assert.ok(metamaskController.preferencesController.setSeedWords.calledOnce)
assert.deepEqual(metamaskController.preferencesController.setSeedWords.args, [[null]])
done()
})
const getConfigSeed = metamaskController.configManager.getSeedWords()
assert.equal(getConfigSeed, null)
})
})
describe('#setCurrentLocale', function () {
@ -793,24 +789,24 @@ describe('MetaMaskController', function () {
describe('#markAccountsFound', function () {
it('adds lost accounts to config manager data', function () {
metamaskController.markAccountsFound(noop)
const configManagerData = metamaskController.configManager.getData()
assert.deepEqual(configManagerData.lostAccounts, [])
const state = metamaskController.getState()
assert.deepEqual(state.lostAccounts, [])
})
})
describe('#markPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to true', function () {
metamaskController.markPasswordForgotten(noop)
const configManagerData = metamaskController.configManager.getData()
assert.equal(configManagerData.forgottenPassword, true)
const state = metamaskController.getState()
assert.equal(state.forgottenPassword, true)
})
})
describe('#unMarkPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to false', function () {
metamaskController.unMarkPasswordForgotten(noop)
const configManagerData = metamaskController.configManager.getData()
assert.equal(configManagerData.forgottenPassword, false)
const state = metamaskController.getState()
assert.equal(state.forgottenPassword, false)
})
})

@ -1,16 +1,11 @@
const assert = require('assert')
const configManagerGen = require('../../../lib/mock-config-manager')
const NoticeController = require('../../../../app/scripts/notice-controller')
describe('notice-controller', function () {
var noticeController
beforeEach(function () {
// simple localStorage polyfill
const configManager = configManagerGen()
noticeController = new NoticeController({
configManager: configManager,
})
noticeController = new NoticeController()
})
describe('notices', function () {

@ -449,5 +449,35 @@ describe('preferences controller', function () {
assert.ok(assetImages[address], `set image correctly`)
})
})
describe('setPasswordForgotten', function () {
it('should default to false', function () {
const state = preferencesController.store.getState()
assert.equal(state.forgottenPassword, false)
})
it('should set the forgottenPassword property in state', function () {
assert.equal(preferencesController.store.getState().forgottenPassword, false)
preferencesController.setPasswordForgotten(true)
assert.equal(preferencesController.store.getState().forgottenPassword, true)
})
})
describe('setSeedWords', function () {
it('should default to null', function () {
const state = preferencesController.store.getState()
assert.equal(state.seedWords, null)
})
it('should set the seedWords property in state', function () {
assert.equal(preferencesController.store.getState().seedWords, null)
preferencesController.setSeedWords('foo bar baz')
assert.equal(preferencesController.store.getState().seedWords, 'foo bar baz')
})
})
})

@ -1,112 +0,0 @@
const assert = require('assert')
const configManagerGen = require('../lib/mock-config-manager')
describe('config-manager', function () {
var configManager
beforeEach(function () {
configManager = configManagerGen()
})
describe('#setConfig', function () {
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('wallet nicknames', function () {
it('should return null when no nicknames are saved', function () {
var nick = configManager.nicknameForWallet('0x0')
assert.equal(nick, null, 'no nickname returned')
})
it('should persist nicknames', function () {
var account = '0x0'
var nick1 = 'foo'
var nick2 = 'bar'
configManager.setNicknameForWallet(account, nick1)
var result1 = configManager.nicknameForWallet(account)
assert.equal(result1, nick1)
configManager.setNicknameForWallet(account, nick2)
var result2 = configManager.nicknameForWallet(account)
assert.equal(result2, nick2)
})
})
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)
})
})
describe('transactions', function () {
beforeEach(function () {
configManager.setTxList([])
})
describe('#getTxList', function () {
it('when new should return empty array', function () {
var result = configManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 0)
})
})
describe('#setTxList', function () {
it('saves the submitted data to the tx list', function () {
var target = [{ foo: 'bar' }]
configManager.setTxList(target)
var result = configManager.getTxList()
assert.equal(result[0].foo, 'bar')
})
})
})
})
Loading…
Cancel
Save