Merge pull request #999 from MetaMask/obs-store2

background - introduce ObservableStore (mark II)
feature/default_network_editable
kumavis 8 years ago committed by GitHub
commit d30612a216
  1. 139
      app/scripts/background.js
  2. 11
      app/scripts/first-time-state.js
  3. 71
      app/scripts/lib/config-manager.js
  4. 73
      app/scripts/lib/inpage-provider.js
  5. 5
      app/scripts/lib/migrations.js
  6. 40
      app/scripts/lib/migrator/index.js
  7. 97
      app/scripts/lib/remote-store.js
  8. 61
      app/scripts/metamask-controller.js
  9. 15
      app/scripts/migrations/002.js
  10. 16
      app/scripts/migrations/003.js
  11. 17
      app/scripts/migrations/004.js
  12. 51
      app/scripts/migrations/_multi-keyring.js
  13. 18
      app/scripts/migrations/index.js
  14. 96
      mock-dev.js
  15. 3
      package.json
  16. 61
      test/integration/lib/idStore-migrator-test.js
  17. 60
      test/lib/mock-config-manager.js
  18. 26
      test/unit/config-manager-test.js
  19. 8
      test/unit/idStore-migration-test.js
  20. 6
      test/unit/metamask-controller-test.js
  21. 46
      test/unit/migrations-test.js

@ -1,37 +1,87 @@
const urlUtil = require('url') const urlUtil = require('url')
const extend = require('xtend')
const Dnode = require('dnode') const Dnode = require('dnode')
const eos = require('end-of-stream') const eos = require('end-of-stream')
const asyncQ = require('async-q')
const pipe = require('pump')
const LocalStorageStore = require('obs-store/lib/localStorage')
const storeTransform = require('obs-store/lib/transform')
const Migrator = require('./lib/migrator/')
const migrations = require('./migrations/')
const PortStream = require('./lib/port-stream.js') const PortStream = require('./lib/port-stream.js')
const notification = require('./lib/notifications.js') const notification = require('./lib/notifications.js')
const messageManager = require('./lib/message-manager') const messageManager = require('./lib/message-manager')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
const MetamaskController = require('./metamask-controller') const MetamaskController = require('./metamask-controller')
const extension = require('./lib/extension') const extension = require('./lib/extension')
const firstTimeState = require('./first-time-state')
const STORAGE_KEY = 'metamask-config' const STORAGE_KEY = 'metamask-config'
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
var popupIsOpen = false
let popupIsOpen = false
// state persistence
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
// initialization flow
asyncQ.waterfall([
() => loadStateFromPersistence(),
(initState) => setupController(initState),
])
.then(() => console.log('MetaMask initialization complete.'))
.catch((err) => { console.error(err) })
//
// State and Persistence
//
function loadStateFromPersistence() {
// migrations
let migrator = new Migrator({ migrations })
let initialState = migrator.generateInitialState(firstTimeState)
return asyncQ.waterfall([
// read from disk
() => Promise.resolve(diskStore.getState() || initialState),
// migrate data
(versionedData) => migrator.migrateData(versionedData),
// write to disk
(versionedData) => {
diskStore.putState(versionedData)
return Promise.resolve(versionedData)
},
// resolve to just data
(versionedData) => Promise.resolve(versionedData.data),
])
}
function setupController (initState) {
//
// MetaMask Controller
//
const controller = new MetamaskController({ const controller = new MetamaskController({
// User confirmation callbacks: // User confirmation callbacks:
showUnconfirmedMessage: triggerUi, showUnconfirmedMessage: triggerUi,
unlockAccountMessage: triggerUi, unlockAccountMessage: triggerUi,
showUnapprovedTx: triggerUi, showUnapprovedTx: triggerUi,
// Persistence Methods: // initial state
setData, initState,
loadData,
}) })
global.metamaskController = controller
function triggerUi () {
if (!popupIsOpen) notification.show() // setup state persistence
} pipe(
// On first install, open a window to MetaMask website to how-it-works. controller.store,
extension.runtime.onInstalled.addListener(function (details) { storeTransform(versionifyData),
if ((details.reason === 'install') && (!METAMASK_DEBUG)) { diskStore
extension.tabs.create({url: 'https://metamask.io/#how-it-works'}) )
function versionifyData(state) {
let versionedData = diskStore.getState()
versionedData.data = state
return versionedData
} }
})
// //
// connect to other contexts // connect to other contexts
@ -90,12 +140,13 @@ function setupControllerConnection (stream) {
} }
// //
// plugin badge text // User Interface setup
// //
controller.txManager.on('updateBadge', updateBadge)
updateBadge() updateBadge()
controller.txManager.on('updateBadge', updateBadge)
// plugin badge text
function updateBadge () { function updateBadge () {
var label = '' var label = ''
var unapprovedTxCount = controller.txManager.unapprovedTxCount var unapprovedTxCount = controller.txManager.unapprovedTxCount
@ -109,54 +160,22 @@ function updateBadge () {
extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' }) extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' })
} }
// data :: setters/getters return Promise.resolve()
function loadData () {
var oldData = getOldStyleData()
var newData
try {
newData = JSON.parse(window.localStorage[STORAGE_KEY])
} catch (e) {}
var data = extend({
meta: {
version: 0,
},
data: {
config: {
provider: {
type: 'testnet',
},
},
},
}, oldData || null, newData || null)
return data
} }
function getOldStyleData () { //
var config, wallet, seedWords // Etc...
//
var result = {
meta: { version: 0 },
data: {},
}
try { // popup trigger
config = JSON.parse(window.localStorage['config']) function triggerUi () {
result.data.config = config if (!popupIsOpen) notification.show()
} 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
} }
function setData (data) { // On first install, open a window to MetaMask website to how-it-works.
window.localStorage[STORAGE_KEY] = JSON.stringify(data) extension.runtime.onInstalled.addListener(function (details) {
if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
} }
})

@ -0,0 +1,11 @@
//
// The default state of MetaMask
//
module.exports = {
config: {
provider: {
type: 'testnet',
},
},
}

@ -1,6 +1,4 @@
const Migrator = require('pojo-migrator')
const MetamaskConfig = require('../config.js') const MetamaskConfig = require('../config.js')
const migrations = require('./migrations')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const normalize = require('./sig-util').normalize const normalize = require('./sig-util').normalize
@ -19,41 +17,18 @@ module.exports = ConfigManager
function ConfigManager (opts) { function ConfigManager (opts) {
// ConfigManager is observable and will emit updates // ConfigManager is observable and will emit updates
this._subs = [] this._subs = []
this.store = opts.store
/* 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: migrations,
// How to load initial config.
// Includes step on migrating pre-pojo-migrator data.
loadData: opts.loadData,
// How to persist migrated config.
setData: opts.setData,
})
} }
ConfigManager.prototype.setConfig = function (config) { ConfigManager.prototype.setConfig = function (config) {
var data = this.migrator.getData() var data = this.getData()
data.config = config data.config = config
this.setData(data) this.setData(data)
this._emitUpdates(config) this._emitUpdates(config)
} }
ConfigManager.prototype.getConfig = function () { ConfigManager.prototype.getConfig = function () {
var data = this.migrator.getData() var data = this.getData()
if ('config' in data) { if ('config' in data) {
return data.config return data.config
} else { } else {
@ -96,15 +71,15 @@ ConfigManager.prototype.getProvider = function () {
} }
ConfigManager.prototype.setData = function (data) { ConfigManager.prototype.setData = function (data) {
this.migrator.saveData(data) this.store.putState(data)
} }
ConfigManager.prototype.getData = function () { ConfigManager.prototype.getData = function () {
return this.migrator.getData() return this.store.getState()
} }
ConfigManager.prototype.setWallet = function (wallet) { ConfigManager.prototype.setWallet = function (wallet) {
var data = this.migrator.getData() var data = this.getData()
data.wallet = wallet data.wallet = wallet
this.setData(data) this.setData(data)
} }
@ -121,11 +96,11 @@ ConfigManager.prototype.getVault = function () {
} }
ConfigManager.prototype.getKeychains = function () { ConfigManager.prototype.getKeychains = function () {
return this.migrator.getData().keychains || [] return this.getData().keychains || []
} }
ConfigManager.prototype.setKeychains = function (keychains) { ConfigManager.prototype.setKeychains = function (keychains) {
var data = this.migrator.getData() var data = this.getData()
data.keychains = keychains data.keychains = keychains
this.setData(data) this.setData(data)
} }
@ -142,19 +117,19 @@ ConfigManager.prototype.setSelectedAccount = function (address) {
} }
ConfigManager.prototype.getWallet = function () { ConfigManager.prototype.getWallet = function () {
return this.migrator.getData().wallet return this.getData().wallet
} }
// Takes a boolean // Takes a boolean
ConfigManager.prototype.setShowSeedWords = function (should) { ConfigManager.prototype.setShowSeedWords = function (should) {
var data = this.migrator.getData() var data = this.getData()
data.showSeedWords = should data.showSeedWords = should
this.setData(data) this.setData(data)
} }
ConfigManager.prototype.getShouldShowSeedWords = function () { ConfigManager.prototype.getShouldShowSeedWords = function () {
var data = this.migrator.getData() var data = this.getData()
return data.showSeedWords return data.showSeedWords
} }
@ -166,7 +141,7 @@ ConfigManager.prototype.setSeedWords = function (words) {
ConfigManager.prototype.getSeedWords = function () { ConfigManager.prototype.getSeedWords = function () {
var data = this.getData() var data = this.getData()
return ('seedWords' in data) && data.seedWords return data.seedWords
} }
ConfigManager.prototype.getCurrentRpcAddress = function () { ConfigManager.prototype.getCurrentRpcAddress = function () {
@ -188,16 +163,12 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
} }
} }
ConfigManager.prototype.setData = function (data) {
this.migrator.saveData(data)
}
// //
// Tx // Tx
// //
ConfigManager.prototype.getTxList = function () { ConfigManager.prototype.getTxList = function () {
var data = this.migrator.getData() var data = this.getData()
if (data.transactions !== undefined) { if (data.transactions !== undefined) {
return data.transactions return data.transactions
} else { } else {
@ -206,7 +177,7 @@ ConfigManager.prototype.getTxList = function () {
} }
ConfigManager.prototype.setTxList = function (txList) { ConfigManager.prototype.setTxList = function (txList) {
var data = this.migrator.getData() var data = this.getData()
data.transactions = txList data.transactions = txList
this.setData(data) this.setData(data)
} }
@ -239,7 +210,7 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
ConfigManager.prototype.getSalt = function () { ConfigManager.prototype.getSalt = function () {
var data = this.getData() var data = this.getData()
return ('salt' in data) && data.salt return data.salt
} }
ConfigManager.prototype.setSalt = function (salt) { ConfigManager.prototype.setSalt = function (salt) {
@ -273,7 +244,7 @@ ConfigManager.prototype.setConfirmedDisclaimer = function (confirmed) {
ConfigManager.prototype.getConfirmedDisclaimer = function () { ConfigManager.prototype.getConfirmedDisclaimer = function () {
var data = this.getData() var data = this.getData()
return ('isDisclaimerConfirmed' in data) && data.isDisclaimerConfirmed return data.isDisclaimerConfirmed
} }
ConfigManager.prototype.setTOSHash = function (hash) { ConfigManager.prototype.setTOSHash = function (hash) {
@ -284,7 +255,7 @@ ConfigManager.prototype.setTOSHash = function (hash) {
ConfigManager.prototype.getTOSHash = function () { ConfigManager.prototype.getTOSHash = function () {
var data = this.getData() var data = this.getData()
return ('TOSHash' in data) && data.TOSHash return data.TOSHash
} }
ConfigManager.prototype.setCurrentFiat = function (currency) { ConfigManager.prototype.setCurrentFiat = function (currency) {
@ -295,7 +266,7 @@ ConfigManager.prototype.setCurrentFiat = function (currency) {
ConfigManager.prototype.getCurrentFiat = function () { ConfigManager.prototype.getCurrentFiat = function () {
var data = this.getData() var data = this.getData()
return ('fiatCurrency' in data) && data.fiatCurrency return data.fiatCurrency
} }
ConfigManager.prototype.updateConversionRate = function () { ConfigManager.prototype.updateConversionRate = function () {
@ -326,12 +297,12 @@ ConfigManager.prototype.setConversionDate = function (datestring) {
ConfigManager.prototype.getConversionRate = function () { ConfigManager.prototype.getConversionRate = function () {
var data = this.getData() var data = this.getData()
return (('conversionRate' in data) && data.conversionRate) || 0 return (data.conversionRate) || 0
} }
ConfigManager.prototype.getConversionDate = function () { ConfigManager.prototype.getConversionDate = function () {
var data = this.getData() var data = this.getData()
return (('conversionDate' in data) && data.conversionDate) || 'N/A' return (data.conversionDate) || 'N/A'
} }
ConfigManager.prototype.getShapeShiftTxList = function () { ConfigManager.prototype.getShapeShiftTxList = function () {
@ -370,7 +341,7 @@ ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositTy
ConfigManager.prototype.getGasMultiplier = function () { ConfigManager.prototype.getGasMultiplier = function () {
var data = this.getData() var data = this.getData()
return ('gasMultiplier' in data) && data.gasMultiplier return data.gasMultiplier
} }
ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) { ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) {

@ -1,7 +1,7 @@
const Streams = require('mississippi') const pipe = require('pump')
const StreamProvider = require('web3-stream-provider') const StreamProvider = require('web3-stream-provider')
const LocalStorageStore = require('obs-store')
const ObjectMultiplex = require('./obj-multiplex') const ObjectMultiplex = require('./obj-multiplex')
const RemoteStore = require('./remote-store.js').RemoteStore
const createRandomId = require('./random-id') const createRandomId = require('./random-id')
module.exports = MetamaskInpageProvider module.exports = MetamaskInpageProvider
@ -10,33 +10,30 @@ function MetamaskInpageProvider (connectionStream) {
const self = this const self = this
// setup connectionStream multiplexing // setup connectionStream multiplexing
var multiStream = ObjectMultiplex() var multiStream = self.multiStream = ObjectMultiplex()
Streams.pipe(connectionStream, multiStream, connectionStream, function (err) { pipe(
let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask' connectionStream,
if (err) warningMsg += '\n' + err.stack multiStream,
console.warn(warningMsg) connectionStream,
}) (err) => logStreamDisconnectWarning('MetaMask', err)
self.multiStream = multiStream )
// subscribe to metamask public config // subscribe to metamask public config (one-way)
var publicConfigStore = remoteStoreWithLocalStorageCache('MetaMask-Config') self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' })
var storeStream = publicConfigStore.createStream() pipe(
Streams.pipe(storeStream, multiStream.createStream('publicConfig'), storeStream, function (err) { multiStream.createStream('publicConfig'),
let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask publicConfig' self.publicConfigStore,
if (err) warningMsg += '\n' + err.stack (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err)
console.warn(warningMsg) )
})
self.publicConfigStore = publicConfigStore
// connect to async provider // connect to async provider
var asyncProvider = new StreamProvider() const asyncProvider = self.asyncProvider = new StreamProvider()
Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) { pipe(
let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask provider' asyncProvider,
if (err) warningMsg += '\n' + err.stack multiStream.createStream('provider'),
console.warn(warningMsg) asyncProvider,
}) (err) => logStreamDisconnectWarning('MetaMask RpcProvider', err)
asyncProvider.on('error', console.error.bind(console)) )
self.asyncProvider = asyncProvider
self.idMap = {} self.idMap = {}
// handle sendAsync requests via asyncProvider // handle sendAsync requests via asyncProvider
@ -72,13 +69,13 @@ MetamaskInpageProvider.prototype.send = function (payload) {
case 'eth_accounts': case 'eth_accounts':
// read from localStorage // read from localStorage
selectedAccount = self.publicConfigStore.get('selectedAccount') selectedAccount = self.publicConfigStore.getState().selectedAccount
result = selectedAccount ? [selectedAccount] : [] result = selectedAccount ? [selectedAccount] : []
break break
case 'eth_coinbase': case 'eth_coinbase':
// read from localStorage // read from localStorage
selectedAccount = self.publicConfigStore.get('selectedAccount') selectedAccount = self.publicConfigStore.getState().selectedAccount
result = selectedAccount || '0x0000000000000000000000000000000000000000' result = selectedAccount || '0x0000000000000000000000000000000000000000'
break break
@ -115,18 +112,6 @@ MetamaskInpageProvider.prototype.isMetaMask = true
// util // util
function remoteStoreWithLocalStorageCache (storageKey) {
// read local cache
var initState = JSON.parse(localStorage[storageKey] || '{}')
var store = new RemoteStore(initState)
// cache the latest state locally
store.subscribe(function (state) {
localStorage[storageKey] = JSON.stringify(state)
})
return store
}
function eachJsonMessage (payload, transformFn) { function eachJsonMessage (payload, transformFn) {
if (Array.isArray(payload)) { if (Array.isArray(payload)) {
return payload.map(transformFn) return payload.map(transformFn)
@ -135,4 +120,10 @@ function eachJsonMessage (payload, transformFn) {
} }
} }
function logStreamDisconnectWarning(remoteLabel, err){
let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}`
if (err) warningMsg += '\n' + err.stack
console.warn(warningMsg)
}
function noop () {} function noop () {}

@ -1,5 +0,0 @@
module.exports = [
require('../migrations/002'),
require('../migrations/003'),
require('../migrations/004'),
]

@ -0,0 +1,40 @@
const asyncQ = require('async-q')
class Migrator {
constructor (opts = {}) {
let migrations = opts.migrations || []
this.migrations = migrations.sort((a, b) => a.version - b.version)
let lastMigration = this.migrations.slice(-1)[0]
// use specified defaultVersion or highest migration version
this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0
}
// run all pending migrations on meta in place
migrateData (versionedData = this.generateInitialState()) {
let remaining = this.migrations.filter(migrationIsPending)
return (
asyncQ.eachSeries(remaining, (migration) => migration.migrate(versionedData))
.then(() => versionedData)
)
// migration is "pending" if hit has a higher
// version number than currentVersion
function migrationIsPending(migration) {
return migration.version > versionedData.meta.version
}
}
generateInitialState (initState) {
return {
meta: {
version: this.defaultVersion,
},
data: initState,
}
}
}
module.exports = Migrator

@ -1,97 +0,0 @@
const Dnode = require('dnode')
const inherits = require('util').inherits
module.exports = {
HostStore: HostStore,
RemoteStore: RemoteStore,
}
function BaseStore (initState) {
this._state = initState || {}
this._subs = []
}
BaseStore.prototype.set = function (key, value) {
throw Error('Not implemented.')
}
BaseStore.prototype.get = function (key) {
return this._state[key]
}
BaseStore.prototype.subscribe = function (fn) {
this._subs.push(fn)
var unsubscribe = this.unsubscribe.bind(this, fn)
return unsubscribe
}
BaseStore.prototype.unsubscribe = function (fn) {
var index = this._subs.indexOf(fn)
if (index !== -1) this._subs.splice(index, 1)
}
BaseStore.prototype._emitUpdates = function (state) {
this._subs.forEach(function (handler) {
handler(state)
})
}
//
// host
//
inherits(HostStore, BaseStore)
function HostStore (initState, opts) {
BaseStore.call(this, initState)
}
HostStore.prototype.set = function (key, value) {
this._state[key] = value
process.nextTick(this._emitUpdates.bind(this, this._state))
}
HostStore.prototype.createStream = function () {
var dnode = Dnode({
// update: this._didUpdate.bind(this),
})
dnode.on('remote', this._didConnect.bind(this))
return dnode
}
HostStore.prototype._didConnect = function (remote) {
this.subscribe(function (state) {
remote.update(state)
})
remote.update(this._state)
}
//
// remote
//
inherits(RemoteStore, BaseStore)
function RemoteStore (initState, opts) {
BaseStore.call(this, initState)
this._remote = null
}
RemoteStore.prototype.set = function (key, value) {
this._remote.set(key, value)
}
RemoteStore.prototype.createStream = function () {
var dnode = Dnode({
update: this._didUpdate.bind(this),
})
dnode.once('remote', this._didConnect.bind(this))
return dnode
}
RemoteStore.prototype._didConnect = function (remote) {
this._remote = remote
}
RemoteStore.prototype._didUpdate = function (state) {
this._state = state
this._emitUpdates(state)
}

@ -1,13 +1,15 @@
const EventEmitter = require('events') const EventEmitter = require('events')
const extend = require('xtend') const extend = require('xtend')
const promiseToCallback = require('promise-to-callback') const promiseToCallback = require('promise-to-callback')
const pipe = require('pump')
const ObservableStore = require('obs-store')
const storeTransform = require('obs-store/lib/transform')
const EthStore = require('./lib/eth-store') const EthStore = require('./lib/eth-store')
const MetaMaskProvider = require('web3-provider-engine/zero.js') const MetaMaskProvider = require('web3-provider-engine/zero.js')
const KeyringController = require('./keyring-controller') const KeyringController = require('./keyring-controller')
const NoticeController = require('./notice-controller') const NoticeController = require('./notice-controller')
const messageManager = require('./lib/message-manager') const messageManager = require('./lib/message-manager')
const TxManager = require('./transaction-manager') const TxManager = require('./transaction-manager')
const HostStore = require('./lib/remote-store.js').HostStore
const Web3 = require('web3') const Web3 = require('web3')
const ConfigManager = require('./lib/config-manager') const ConfigManager = require('./lib/config-manager')
const extension = require('./lib/extension') const extension = require('./lib/extension')
@ -15,19 +17,30 @@ const autoFaucet = require('./lib/auto-faucet')
const nodeify = require('./lib/nodeify') const nodeify = require('./lib/nodeify')
const IdStoreMigrator = require('./lib/idStore-migrator') const IdStoreMigrator = require('./lib/idStore-migrator')
const accountImporter = require('./account-import-strategies') const accountImporter = require('./account-import-strategies')
const version = require('../manifest.json').version const version = require('../manifest.json').version
module.exports = class MetamaskController extends EventEmitter { module.exports = class MetamaskController extends EventEmitter {
constructor (opts) { constructor (opts) {
super() super()
this.state = { network: 'loading' }
this.opts = opts this.opts = opts
this.configManager = new ConfigManager(opts) this.state = { network: 'loading' }
// observable state store
this.store = new ObservableStore(opts.initState)
// config manager
this.configManager = new ConfigManager({
store: this.store,
})
// key mgmt
this.keyringController = new KeyringController({ this.keyringController = new KeyringController({
configManager: this.configManager, configManager: this.configManager,
getNetwork: this.getStateNetwork.bind(this), getNetwork: this.getStateNetwork.bind(this),
}) })
this.keyringController.on('newAccount', (account) => {
autoFaucet(account)
})
// notices // notices
this.noticeController = new NoticeController({ this.noticeController = new NoticeController({
configManager: this.configManager, configManager: this.configManager,
@ -245,29 +258,23 @@ module.exports = class MetamaskController extends EventEmitter {
initPublicConfigStore () { initPublicConfigStore () {
// get init state // get init state
var initPublicState = configToPublic(this.configManager.getConfig()) const publicConfigStore = new ObservableStore()
var publicConfigStore = new HostStore(initPublicState)
// subscribe to changes
this.configManager.subscribe(function (state) {
storeSetFromObj(publicConfigStore, configToPublic(state))
})
this.keyringController.on('newAccount', (account) => { // sync publicConfigStore with transform
autoFaucet(account) pipe(
}) this.store,
storeTransform(selectPublicState),
publicConfigStore
)
// config substate function selectPublicState(state) {
function configToPublic (state) { const result = { selectedAccount: undefined }
return { try {
selectedAccount: state.selectedAccount, result.selectedAccount = state.config.selectedAccount
} } catch (_) {
// thats fine, im sure it will be there next time...
} }
// dump obj into store return result
function storeSetFromObj (store, obj) {
Object.keys(obj).forEach(function (key) {
store.set(key, obj[key])
})
} }
return publicConfigStore return publicConfigStore
@ -310,9 +317,11 @@ module.exports = class MetamaskController extends EventEmitter {
this.opts.showUnconfirmedMessage(msgParams, msgId) this.opts.showUnconfirmedMessage(msgParams, msgId)
} }
setupPublicConfig (stream) { setupPublicConfig (outStream) {
var storeStream = this.publicConfigStore.createStream() pipe(
stream.pipe(storeStream).pipe(stream) this.publicConfigStore,
outStream
)
} }
// Log blocks // Log blocks

@ -1,13 +1,16 @@
const version = 2
module.exports = { module.exports = {
version: 2, version,
migrate: function (data) { migrate: function (versionedData) {
versionedData.meta.version = version
try { try {
if (data.config.provider.type === 'etherscan') { if (versionedData.data.config.provider.type === 'etherscan') {
data.config.provider.type = 'rpc' versionedData.data.config.provider.type = 'rpc'
data.config.provider.rpcTarget = 'https://rpc.metamask.io/' versionedData.data.config.provider.rpcTarget = 'https://rpc.metamask.io/'
} }
} catch (e) {} } catch (e) {}
return data return Promise.resolve(versionedData)
}, },
} }

@ -1,15 +1,17 @@
var oldTestRpc = 'https://rawtestrpc.metamask.io/' const version = 3
var newTestRpc = 'https://testrpc.metamask.io/' const oldTestRpc = 'https://rawtestrpc.metamask.io/'
const newTestRpc = 'https://testrpc.metamask.io/'
module.exports = { module.exports = {
version: 3, version,
migrate: function (data) { migrate: function (versionedData) {
versionedData.meta.version = version
try { try {
if (data.config.provider.rpcTarget === oldTestRpc) { if (versionedData.data.config.provider.rpcTarget === oldTestRpc) {
data.config.provider.rpcTarget = newTestRpc versionedData.data.config.provider.rpcTarget = newTestRpc
} }
} catch (e) {} } catch (e) {}
return data return Promise.resolve(versionedData)
}, },
} }

@ -1,22 +1,25 @@
const version = 4
module.exports = { module.exports = {
version: 4, version,
migrate: function (data) { migrate: function (versionedData) {
versionedData.meta.version = version
try { try {
if (data.config.provider.type !== 'rpc') return data if (versionedData.data.config.provider.type !== 'rpc') return Promise.resolve(versionedData)
switch (data.config.provider.rpcTarget) { switch (versionedData.data.config.provider.rpcTarget) {
case 'https://testrpc.metamask.io/': case 'https://testrpc.metamask.io/':
data.config.provider = { versionedData.data.config.provider = {
type: 'testnet', type: 'testnet',
} }
break break
case 'https://rpc.metamask.io/': case 'https://rpc.metamask.io/':
data.config.provider = { versionedData.data.config.provider = {
type: 'mainnet', type: 'mainnet',
} }
break break
} }
} catch (_) {} } catch (_) {}
return data return Promise.resolve(versionedData)
}, },
} }

@ -0,0 +1,51 @@
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('../../app/scripts/lib/keyring-controller')
const password = 'obviously not correct'
module.exports = {
version,
migrate: function (versionedData) {
versionedData.meta.version = version
let store = new ObservableStore(versionedData.data)
let configManager = new ConfigManager({ store })
let idStoreMigrator = new IdentityStoreMigrator({ configManager })
let 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)
})
})
},
}

@ -0,0 +1,18 @@
/* The migrator 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.
*/
// 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.
module.exports = [
require('./002'),
require('./003'),
require('./004'),
]

@ -15,97 +15,71 @@
const extend = require('xtend') const extend = require('xtend')
const render = require('react-dom').render const render = require('react-dom').render
const h = require('react-hyperscript') const h = require('react-hyperscript')
const pipe = require('mississippi').pipe
const LocalStorageStore = require('obs-store/lib/localStorage')
const Root = require('./ui/app/root') const Root = require('./ui/app/root')
const configureStore = require('./ui/app/store') const configureStore = require('./ui/app/store')
const actions = require('./ui/app/actions') const actions = require('./ui/app/actions')
const states = require('./development/states') const states = require('./development/states')
const Selector = require('./development/selector') const Selector = require('./development/selector')
const MetamaskController = require('./app/scripts/metamask-controller') const MetamaskController = require('./app/scripts/metamask-controller')
const firstTimeState = require('./app/scripts/first-time-state')
const extension = require('./development/mockExtension') const extension = require('./development/mockExtension')
const noop = function () {}
const STORAGE_KEY = 'metamask-config'
//
// Query String // Query String
//
const qs = require('qs') const qs = require('qs')
let queryString = qs.parse(window.location.href.split('#')[1]) let queryString = qs.parse(window.location.href.split('#')[1])
let selectedView = queryString.view || 'first time' let selectedView = queryString.view || 'first time'
const firstState = states[selectedView] const firstState = states[selectedView]
updateQueryParams(selectedView) updateQueryParams(selectedView)
function updateQueryParams(newView) {
queryString.view = newView
const params = qs.stringify(queryString)
window.location.href = window.location.href.split('#')[0] + `#${params}`
}
//
// CSS // CSS
//
const MetaMaskUiCss = require('./ui/css') const MetaMaskUiCss = require('./ui/css')
const injectCss = require('inject-css') const injectCss = require('inject-css')
//
// MetaMask Controller
//
function updateQueryParams(newView) { let dataStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
queryString.view = newView // initial state for first time users
const params = qs.stringify(queryString) if (!dataStore.getState()) {
window.location.href = window.location.href.split('#')[0] + `#${params}` dataStore.putState(firstTimeState)
} }
const noop = function () {}
const controller = new MetamaskController({ const controller = new MetamaskController({
// User confirmation callbacks: // User confirmation callbacks:
showUnconfirmedMessage: noop, showUnconfirmedMessage: noop,
unlockAccountMessage: noop, unlockAccountMessage: noop,
showUnapprovedTx: noop, showUnapprovedTx: noop,
// Persistence Methods: // initial state
setData, initState: dataStore.getState(),
loadData,
}) })
// Stub out localStorage for non-browser environments // setup state persistence
if (!window.localStorage) { pipe(
window.localStorage = {} controller.store,
} dataStore
const STORAGE_KEY = 'metamask-config' )
function loadData () {
var oldData = getOldStyleData()
var newData
try {
newData = JSON.parse(window.localStorage[STORAGE_KEY])
} catch (e) {}
var data = extend({
meta: {
version: 0,
},
data: {
config: {
provider: {
type: 'testnet',
},
},
},
}, oldData || null, newData || null)
return data
}
function setData (data) {
window.localStorage[STORAGE_KEY] = JSON.stringify(data)
}
function getOldStyleData () {
var config, wallet, seedWords
var result = { //
meta: { version: 0 }, // User Interface
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
}
actions._setBackgroundConnection(controller.getApi()) actions._setBackgroundConnection(controller.getApi())
actions.update = function(stateName) { actions.update = function(stateName) {

@ -37,6 +37,7 @@
}, },
"dependencies": { "dependencies": {
"async": "^1.5.2", "async": "^1.5.2",
"async-q": "^0.3.1",
"bip39": "^2.2.0", "bip39": "^2.2.0",
"browser-passworder": "^2.0.3", "browser-passworder": "^2.0.3",
"browserify-derequire": "^0.9.4", "browserify-derequire": "^0.9.4",
@ -69,6 +70,7 @@
"mississippi": "^1.2.0", "mississippi": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"multiplex": "^6.7.0", "multiplex": "^6.7.0",
"obs-store": "^2.2.3",
"once": "^1.3.3", "once": "^1.3.3",
"ping-pong-stream": "^1.0.0", "ping-pong-stream": "^1.0.0",
"pojo-migrator": "^2.1.0", "pojo-migrator": "^2.1.0",
@ -76,6 +78,7 @@
"post-message-stream": "^1.0.0", "post-message-stream": "^1.0.0",
"promise-filter": "^1.1.0", "promise-filter": "^1.1.0",
"promise-to-callback": "^1.0.0", "promise-to-callback": "^1.0.0",
"pump": "^1.0.2",
"pumpify": "^1.3.4", "pumpify": "^1.3.4",
"qrcode-npm": "0.0.3", "qrcode-npm": "0.0.3",
"react": "^15.0.2", "react": "^15.0.2",

@ -1,30 +1,23 @@
var ConfigManager = require('../../../app/scripts/lib/config-manager') const ObservableStore = require('obs-store')
var IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator') const ConfigManager = require('../../../app/scripts/lib/config-manager')
var SimpleKeyring = require('../../../app/scripts/keyrings/simple') const IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator')
var normalize = require('../../../app/scripts/lib/sig-util').normalize const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
const normalize = require('../../../app/scripts/lib/sig-util').normalize
var oldStyleVault = require('../mocks/oldVault.json') const oldStyleVault = require('../mocks/oldVault.json').data
var badStyleVault = require('../mocks/badVault.json') const badStyleVault = require('../mocks/badVault.json').data
var STORAGE_KEY = 'metamask-config' const PASSWORD = '12345678'
var PASSWORD = '12345678' const FIRST_ADDRESS = '0x4dd5d356c5A016A220bCD69e82e5AF680a430d00'.toLowerCase()
var FIRST_ADDRESS = '0x4dd5d356c5A016A220bCD69e82e5AF680a430d00'.toLowerCase() const BAD_STYLE_FIRST_ADDRESS = '0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9'
var SEED = 'fringe damage bounce extend tunnel afraid alert sound all soldier all dinner' const SEED = 'fringe damage bounce extend tunnel afraid alert sound all soldier all dinner'
var BAD_STYLE_FIRST_ADDRESS = '0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9'
QUnit.module('Old Style Vaults', { QUnit.module('Old Style Vaults', {
beforeEach: function () { beforeEach: function () {
window.localStorage[STORAGE_KEY] = JSON.stringify(oldStyleVault) let managers = managersFromInitState(oldStyleVault)
this.configManager = new ConfigManager({
loadData: () => { return JSON.parse(window.localStorage[STORAGE_KEY]) },
setData: (data) => { window.localStorage[STORAGE_KEY] = JSON.stringify(data) },
})
this.migrator = new IdStoreMigrator({ this.configManager = managers.configManager
configManager: this.configManager, this.migrator = managers.migrator
})
} }
}) })
@ -37,6 +30,7 @@ QUnit.test('migrator:migratedVaultForPassword', function (assert) {
this.migrator.migratedVaultForPassword(PASSWORD) this.migrator.migratedVaultForPassword(PASSWORD)
.then((result) => { .then((result) => {
assert.ok(result, 'migratedVaultForPassword returned result')
const { serialized, lostAccounts } = result const { serialized, lostAccounts } = result
assert.equal(serialized.data.mnemonic, SEED, 'seed phrase recovered') assert.equal(serialized.data.mnemonic, SEED, 'seed phrase recovered')
assert.equal(lostAccounts.length, 0, 'no lost accounts') assert.equal(lostAccounts.length, 0, 'no lost accounts')
@ -46,16 +40,10 @@ QUnit.test('migrator:migratedVaultForPassword', function (assert) {
QUnit.module('Old Style Vaults with bad HD seed', { QUnit.module('Old Style Vaults with bad HD seed', {
beforeEach: function () { beforeEach: function () {
window.localStorage[STORAGE_KEY] = JSON.stringify(badStyleVault) let managers = managersFromInitState(badStyleVault)
this.configManager = new ConfigManager({ this.configManager = managers.configManager
loadData: () => { return JSON.parse(window.localStorage[STORAGE_KEY]) }, this.migrator = managers.migrator
setData: (data) => { window.localStorage[STORAGE_KEY] = JSON.stringify(data) },
})
this.migrator = new IdStoreMigrator({
configManager: this.configManager,
})
} }
}) })
@ -64,6 +52,7 @@ QUnit.test('migrator:migratedVaultForPassword', function (assert) {
this.migrator.migratedVaultForPassword(PASSWORD) this.migrator.migratedVaultForPassword(PASSWORD)
.then((result) => { .then((result) => {
assert.ok(result, 'migratedVaultForPassword returned result')
const { serialized, lostAccounts } = result const { serialized, lostAccounts } = result
assert.equal(lostAccounts.length, 1, 'one lost account') assert.equal(lostAccounts.length, 1, 'one lost account')
@ -89,3 +78,15 @@ QUnit.test('migrator:migratedVaultForPassword', function (assert) {
}) })
}) })
function managersFromInitState(initState){
let configManager = new ConfigManager({
store: new ObservableStore(initState),
})
let migrator = new IdStoreMigrator({
configManager: configManager,
})
return { configManager, migrator }
}

@ -1,58 +1,10 @@
var ConfigManager = require('../../app/scripts/lib/config-manager') 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')
const STORAGE_KEY = 'metamask-config' const STORAGE_KEY = 'metamask-config'
const extend = require('xtend')
module.exports = function() { module.exports = function() {
return new ConfigManager({ loadData, setData }) let store = new ObservableStore(clone(firstTimeState))
} return new ConfigManager({ store })
function loadData () {
var oldData = getOldStyleData()
var newData
try {
newData = JSON.parse(window.localStorage[STORAGE_KEY])
} catch (e) {}
var data = extend({
meta: {
version: 0,
},
data: {
config: {
provider: {
type: 'testnet',
},
},
},
}, oldData || null, 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
}
function setData (data) {
window.localStorage[STORAGE_KEY] = JSON.stringify(data)
} }

@ -1,24 +1,23 @@
// polyfill fetch // polyfill fetch
global.fetch = global.fetch || require('isomorphic-fetch') global.fetch = global.fetch || require('isomorphic-fetch')
const assert = require('assert') const assert = require('assert')
const extend = require('xtend') const extend = require('xtend')
const rp = require('request-promise') const rp = require('request-promise')
const nock = require('nock') const nock = require('nock')
const configManagerGen = require('../lib/mock-config-manager') const configManagerGen = require('../lib/mock-config-manager')
const STORAGE_KEY = 'metamask-persistance-key'
describe('config-manager', function() { describe('config-manager', function() {
var configManager var configManager
beforeEach(function() { beforeEach(function() {
window.localStorage = {} // Hacking localStorage support into JSDom
configManager = configManagerGen() configManager = configManagerGen()
}) })
describe('currency conversions', function() { describe('currency conversions', function() {
describe('#getCurrentFiat', function() { describe('#getCurrentFiat', function() {
it('should return false if no previous key exists', function() { it('should return undefined if no previous key exists', function() {
var result = configManager.getCurrentFiat() var result = configManager.getCurrentFiat()
assert.ok(!result) assert.ok(!result)
}) })
@ -26,14 +25,14 @@ describe('config-manager', function() {
describe('#setCurrentFiat', function() { describe('#setCurrentFiat', function() {
it('should make getCurrentFiat return true once set', function() { it('should make getCurrentFiat return true once set', function() {
assert.equal(configManager.getCurrentFiat(), false) assert.equal(configManager.getCurrentFiat(), undefined)
configManager.setCurrentFiat('USD') configManager.setCurrentFiat('USD')
var result = configManager.getCurrentFiat() var result = configManager.getCurrentFiat()
assert.equal(result, 'USD') assert.equal(result, 'USD')
}) })
it('should work with other currencies as well', function() { it('should work with other currencies as well', function() {
assert.equal(configManager.getCurrentFiat(), false) assert.equal(configManager.getCurrentFiat(), undefined)
configManager.setCurrentFiat('JPY') configManager.setCurrentFiat('JPY')
var result = configManager.getCurrentFiat() var result = configManager.getCurrentFiat()
assert.equal(result, 'JPY') assert.equal(result, 'JPY')
@ -41,7 +40,7 @@ describe('config-manager', function() {
}) })
describe('#getConversionRate', function() { describe('#getConversionRate', function() {
it('should return false if non-existent', function() { it('should return undefined if non-existent', function() {
var result = configManager.getConversionRate() var result = configManager.getConversionRate()
assert.ok(!result) assert.ok(!result)
}) })
@ -54,7 +53,7 @@ describe('config-manager', function() {
.get('/api/ticker/eth-USD') .get('/api/ticker/eth-USD')
.reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}') .reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
assert.equal(configManager.getConversionRate(), false) assert.equal(configManager.getConversionRate(), 0)
var promise = new Promise( var promise = new Promise(
function (resolve, reject) { function (resolve, reject) {
configManager.setCurrentFiat('USD') configManager.setCurrentFiat('USD')
@ -75,7 +74,7 @@ describe('config-manager', function() {
it('should work for JPY as well.', function() { it('should work for JPY as well.', function() {
this.timeout(15000) this.timeout(15000)
assert.equal(configManager.getConversionRate(), false) assert.equal(configManager.getConversionRate(), 0)
var jpyMock = nock('https://www.cryptonator.com') var jpyMock = nock('https://www.cryptonator.com')
.get('/api/ticker/eth-JPY') .get('/api/ticker/eth-JPY')
@ -103,7 +102,7 @@ describe('config-manager', function() {
describe('confirmation', function() { describe('confirmation', function() {
describe('#getConfirmedDisclaimer', function() { describe('#getConfirmedDisclaimer', function() {
it('should return false if no previous key exists', function() { it('should return undefined if no previous key exists', function() {
var result = configManager.getConfirmedDisclaimer() var result = configManager.getConfirmedDisclaimer()
assert.ok(!result) assert.ok(!result)
}) })
@ -111,16 +110,16 @@ describe('config-manager', function() {
describe('#setConfirmedDisclaimer', function() { describe('#setConfirmedDisclaimer', function() {
it('should make getConfirmedDisclaimer return true once set', function() { it('should make getConfirmedDisclaimer return true once set', function() {
assert.equal(configManager.getConfirmedDisclaimer(), false) assert.equal(configManager.getConfirmedDisclaimer(), undefined)
configManager.setConfirmedDisclaimer(true) configManager.setConfirmedDisclaimer(true)
var result = configManager.getConfirmedDisclaimer() var result = configManager.getConfirmedDisclaimer()
assert.equal(result, true) assert.equal(result, true)
}) })
it('should be able to set false', function() { it('should be able to set undefined', function() {
configManager.setConfirmedDisclaimer(false) configManager.setConfirmedDisclaimer(undefined)
var result = configManager.getConfirmedDisclaimer() var result = configManager.getConfirmedDisclaimer()
assert.equal(result, false) assert.equal(result, undefined)
}) })
it('should persist to local storage', function() { it('should persist to local storage', function() {
@ -132,7 +131,6 @@ describe('config-manager', function() {
}) })
describe('#setConfig', function() { describe('#setConfig', function() {
window.localStorage = {} // Hacking localStorage support into JSDom
it('should set the config key', function () { it('should set the config key', function () {
var testConfig = { var testConfig = {

@ -1,5 +1,6 @@
const async = require('async') const async = require('async')
const assert = require('assert') const assert = require('assert')
const ObservableStore = require('obs-store')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
const ConfigManager = require('../../app/scripts/lib/config-manager') const ConfigManager = require('../../app/scripts/lib/config-manager')
@ -42,10 +43,9 @@ describe('IdentityStore to KeyringController migration', function() {
beforeEach(function(done) { beforeEach(function(done) {
this.sinon = sinon.sandbox.create() this.sinon = sinon.sandbox.create()
window.localStorage = {} // Hacking localStorage support into JSDom window.localStorage = {} // Hacking localStorage support into JSDom
configManager = new ConfigManager({ let store = new ObservableStore(loadData())
loadData, store.subscribe(setData)
setData: (d) => { window.localStorage = d } configManager = new ConfigManager({ store })
})
idStore = new IdentityStore({ idStore = new IdentityStore({

@ -10,9 +10,11 @@ describe('MetaMaskController', function() {
showUnconfirmedMessage: noop, showUnconfirmedMessage: noop,
unlockAccountMessage: noop, unlockAccountMessage: noop,
showUnapprovedTx: noop, showUnapprovedTx: noop,
setData, // initial state
loadData, initState: loadData(),
}) })
// setup state persistence
controller.store.subscribe(setData)
beforeEach(function() { beforeEach(function() {
// sinon allows stubbing methods that are easily verified // sinon allows stubbing methods that are easily verified

@ -1,34 +1,34 @@
var assert = require('assert') const assert = require('assert')
var path = require('path') const path = require('path')
var wallet1 = require(path.join('..', 'lib', 'migrations', '001.json')) const wallet1 = require(path.join('..', 'lib', 'migrations', '001.json'))
var migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002')) const migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002'))
var migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003')) const migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003'))
var migration4 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '004')) const migration4 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '004'))
describe('wallet1 is migrated successfully', function() { const oldTestRpc = 'https://rawtestrpc.metamask.io/'
const newTestRpc = 'https://testrpc.metamask.io/'
it('should convert providers', function(done) { describe('wallet1 is migrated successfully', function() {
it('should convert providers', function() {
wallet1.data.config.provider = { type: 'etherscan', rpcTarget: null } wallet1.data.config.provider = { type: 'etherscan', rpcTarget: null }
var firstResult = migration2.migrate(wallet1.data) return migration2.migrate(wallet1)
assert.equal(firstResult.config.provider.type, 'rpc', 'provider should be rpc') .then((firstResult) => {
assert.equal(firstResult.config.provider.rpcTarget, 'https://rpc.metamask.io/', 'main provider should be our rpc') assert.equal(firstResult.data.config.provider.type, 'rpc', 'provider should be rpc')
assert.equal(firstResult.data.config.provider.rpcTarget, 'https://rpc.metamask.io/', 'main provider should be our rpc')
var oldTestRpc = 'https://rawtestrpc.metamask.io/' firstResult.data.config.provider.rpcTarget = oldTestRpc
var newTestRpc = 'https://testrpc.metamask.io/' return migration3.migrate(firstResult)
firstResult.config.provider.rpcTarget = oldTestRpc }).then((secondResult) => {
assert.equal(secondResult.data.config.provider.rpcTarget, newTestRpc)
var secondResult = migration3.migrate(firstResult) return migration4.migrate(secondResult)
assert.equal(secondResult.config.provider.rpcTarget, newTestRpc) }).then((thirdResult) => {
assert.equal(thirdResult.data.config.provider.rpcTarget, null)
var thirdResult = migration4.migrate(secondResult) assert.equal(thirdResult.data.config.provider.type, 'testnet')
assert.equal(secondResult.config.provider.rpcTarget, null) })
assert.equal(secondResult.config.provider.type, 'testnet')
done()
}) })
}) })

Loading…
Cancel
Save