1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

migrations - introduce promise-based migrator

This commit is contained in:
kumavis 2017-01-12 01:17:05 -08:00
parent 3bc996878b
commit b33c51c0a6
7 changed files with 212 additions and 144 deletions

View File

@ -1,7 +1,8 @@
const urlUtil = require('url')
const Dnode = require('dnode')
const eos = require('end-of-stream')
const Migrator = require('pojo-migrator')
const asyncQ = require('async-q')
const Migrator = require('./lib/migrator/')
const migrations = require('./lib/migrations')
const LocalStorageStore = require('./lib/observable/local-storage')
const PortStream = require('./lib/port-stream.js')
@ -16,27 +17,44 @@ const STORAGE_KEY = 'metamask-config'
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
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
//
// state persistence
let dataStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
// initial state for first time users
if (!dataStore.get()) {
dataStore.put({ meta: { version: 0 }, data: firstTimeState })
function loadStateFromPersistence() {
// migrations
let migrator = new Migrator({ migrations })
let initialState = {
meta: { version: migrator.defaultVersion },
data: firstTimeState,
}
return asyncQ.waterfall([
// read from disk
() => Promise.resolve(diskStore.get() || initialState),
// migrate data
(versionedData) => migrator.migrateData(versionedData),
// write to disk
(versionedData) => {
diskStore.put(versionedData)
return Promise.resolve(versionedData)
},
// resolve to just data
(versionedData) => Promise.resolve(versionedData.data),
])
}
// migrations
let migrator = new Migrator({
migrations,
// Data persistence methods
loadData: () => dataStore.get(),
setData: (newState) => dataStore.put(newState),
})
function setupController (initState) {
//
// MetaMask Controller
@ -48,10 +66,11 @@ const controller = new MetamaskController({
unlockAccountMessage: triggerUi,
showUnapprovedTx: triggerUi,
// initial state
initState: migrator.getData(),
initState,
})
// setup state persistence
controller.store.subscribe((newState) => migrator.saveData(newState))
controller.store.subscribe((newState) => diskStore)
//
// connect to other contexts
@ -113,20 +132,9 @@ function setupControllerConnection (stream) {
// User Interface setup
//
// popup trigger
function triggerUi () {
if (!popupIsOpen) notification.show()
}
// On first install, open a window to MetaMask website to how-it-works.
extension.runtime.onInstalled.addListener(function (details) {
if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
}
})
controller.txManager.on('updateBadge', updateBadge)
// plugin badge text
controller.txManager.on('updateBadge', updateBadge)
function updateBadge () {
var label = ''
var unapprovedTxCount = controller.txManager.unapprovedTxCount
@ -139,3 +147,23 @@ function updateBadge () {
extension.browserAction.setBadgeText({ text: label })
extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' })
}
return Promise.resolve()
}
//
// Etc...
//
// popup trigger
function triggerUi () {
if (!popupIsOpen) notification.show()
}
// On first install, open a window to MetaMask website to how-it-works.
extension.runtime.onInstalled.addListener(function (details) {
if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
}
})

View File

@ -0,0 +1,31 @@
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 (meta = { version: this.defaultVersion }) {
let remaining = this.migrations.filter(migrationIsPending)
return (
asyncQ.eachSeries(remaining, (migration) => migration.migrate(meta))
.then(() => meta)
)
// migration is "pending" if hit has a higher
// version number than currentVersion
function migrationIsPending(migration) {
return migration.version > meta.version
}
}
}
module.exports = Migrator

View File

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

View File

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

View File

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

View File

@ -37,6 +37,7 @@
},
"dependencies": {
"async": "^1.5.2",
"async-q": "^0.3.1",
"bip39": "^2.2.0",
"browser-passworder": "^2.0.3",
"browserify-derequire": "^0.9.4",

View File

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