Merge pull request #1062 from MetaMask/kumavis-refactor3

Kumavis refactor3
This commit is contained in:
kumavis 2017-01-28 22:26:47 -08:00 committed by GitHub
commit 47b48c0e02
12 changed files with 318 additions and 380 deletions

View File

@ -1,12 +1,14 @@
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const bip39 = require('bip39') const bip39 = require('bip39')
const EventEmitter = require('events').EventEmitter const EventEmitter = require('events').EventEmitter
const ObservableStore = require('obs-store')
const filter = require('promise-filter') const filter = require('promise-filter')
const encryptor = require('browser-passworder') const encryptor = require('browser-passworder')
const createId = require('./lib/random-id')
const normalize = require('./lib/sig-util').normalize const normalizeAddress = require('./lib/sig-util').normalize
const messageManager = require('./lib/message-manager') const messageManager = require('./lib/message-manager')
const BN = ethUtil.BN function noop () {}
// Keyrings: // Keyrings:
const SimpleKeyring = require('./keyrings/simple') const SimpleKeyring = require('./keyrings/simple')
@ -16,9 +18,8 @@ const keyringTypes = [
HdKeyring, HdKeyring,
] ]
const createId = require('./lib/random-id')
module.exports = class KeyringController extends EventEmitter { class KeyringController extends EventEmitter {
// PUBLIC METHODS // PUBLIC METHODS
// //
@ -29,6 +30,8 @@ module.exports = class KeyringController extends EventEmitter {
constructor (opts) { constructor (opts) {
super() super()
const initState = opts.initState || {}
this.store = new ObservableStore(initState)
this.configManager = opts.configManager this.configManager = opts.configManager
this.ethStore = opts.ethStore this.ethStore = opts.ethStore
this.encryptor = encryptor this.encryptor = encryptor
@ -71,29 +74,31 @@ module.exports = class KeyringController extends EventEmitter {
// in this class, but will need to be Promisified when we move our // in this class, but will need to be Promisified when we move our
// persistence to an async model. // persistence to an async model.
getState () { getState () {
const configManager = this.configManager return Promise.all(this.keyrings.map(this.displayForKeyring))
const address = configManager.getSelectedAccount()
const wallet = configManager.getWallet() // old style vault
const vault = configManager.getVault() // new style vault
const keyrings = this.keyrings
return Promise.all(keyrings.map(this.displayForKeyring))
.then((displayKeyrings) => { .then((displayKeyrings) => {
const state = this.store.getState()
// old wallet
const wallet = this.configManager.getWallet()
return { return {
// computed
isInitialized: (!!wallet || !!state.vault),
isUnlocked: (!!this.password),
keyrings: displayKeyrings,
// hard coded
keyringTypes: this.keyringTypes.map(krt => krt.type),
// memStore
identities: this.identities,
// diskStore
selectedAccount: state.selectedAccount,
// configManager
seedWords: this.configManager.getSeedWords(), seedWords: this.configManager.getSeedWords(),
isInitialized: (!!wallet || !!vault),
isUnlocked: Boolean(this.password),
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(),
unconfMsgs: messageManager.unconfirmedMsgs(),
messages: messageManager.getMsgList(),
selectedAccount: address,
shapeShiftTxList: this.configManager.getShapeShiftTxList(),
currentFiat: this.configManager.getCurrentFiat(), currentFiat: this.configManager.getCurrentFiat(),
conversionRate: this.configManager.getConversionRate(), conversionRate: this.configManager.getConversionRate(),
conversionDate: this.configManager.getConversionDate(), conversionDate: this.configManager.getConversionDate(),
keyringTypes: this.keyringTypes.map(krt => krt.type), // messageManager
identities: this.identities, unconfMsgs: messageManager.unconfirmedMsgs(),
keyrings: displayKeyrings, messages: messageManager.getMsgList(),
} }
}) })
} }
@ -146,8 +151,8 @@ module.exports = class KeyringController extends EventEmitter {
.then((accounts) => { .then((accounts) => {
const firstAccount = accounts[0] const firstAccount = accounts[0]
if (!firstAccount) throw new Error('KeyringController - First Account not found.') if (!firstAccount) throw new Error('KeyringController - First Account not found.')
const hexAccount = normalize(firstAccount) const hexAccount = normalizeAddress(firstAccount)
this.configManager.setSelectedAccount(hexAccount) this.setSelectedAccount(hexAccount)
return this.setupAccounts(accounts) return this.setupAccounts(accounts)
}) })
.then(this.persistAllKeyrings.bind(this, password)) .then(this.persistAllKeyrings.bind(this, password))
@ -233,9 +238,9 @@ module.exports = class KeyringController extends EventEmitter {
// //
// Sets the state's `selectedAccount` value // Sets the state's `selectedAccount` value
// to the specified address. // to the specified address.
setSelectedAccount (address) { setSelectedAccount (account) {
var addr = normalize(address) var address = normalizeAddress(account)
this.configManager.setSelectedAccount(addr) this.store.updateState({ selectedAccount: address })
return this.fullUpdate() return this.fullUpdate()
} }
@ -247,11 +252,19 @@ module.exports = class KeyringController extends EventEmitter {
// //
// Persists a nickname equal to `label` for the specified account. // Persists a nickname equal to `label` for the specified account.
saveAccountLabel (account, label) { saveAccountLabel (account, label) {
const address = normalize(account) try {
const configManager = this.configManager const hexAddress = normalizeAddress(account)
configManager.setNicknameForWallet(address, label) // update state on diskStore
this.identities[address].name = label const state = this.store.getState()
const walletNicknames = state.walletNicknames || {}
walletNicknames[hexAddress] = label
this.store.updateState({ walletNicknames })
// update state on memStore
this.identities[hexAddress].name = label
return Promise.resolve(label) return Promise.resolve(label)
} catch (err) {
return Promise.reject(err)
}
} }
// Export Account // Export Account
@ -267,7 +280,7 @@ module.exports = class KeyringController extends EventEmitter {
try { try {
return this.getKeyringForAccount(address) return this.getKeyringForAccount(address)
.then((keyring) => { .then((keyring) => {
return keyring.exportAccount(normalize(address)) return keyring.exportAccount(normalizeAddress(address))
}) })
} catch (e) { } catch (e) {
return Promise.reject(e) return Promise.reject(e)
@ -281,7 +294,7 @@ module.exports = class KeyringController extends EventEmitter {
// TX Manager to update the state after signing // TX Manager to update the state after signing
signTransaction (ethTx, _fromAddress) { signTransaction (ethTx, _fromAddress) {
const fromAddress = normalize(_fromAddress) const fromAddress = normalizeAddress(_fromAddress)
return this.getKeyringForAccount(fromAddress) return this.getKeyringForAccount(fromAddress)
.then((keyring) => { .then((keyring) => {
return keyring.signTransaction(fromAddress, ethTx) return keyring.signTransaction(fromAddress, ethTx)
@ -354,7 +367,7 @@ module.exports = class KeyringController extends EventEmitter {
delete msgParams.metamaskId delete msgParams.metamaskId
const approvalCb = this._unconfMsgCbs[msgId] || noop const approvalCb = this._unconfMsgCbs[msgId] || noop
const address = normalize(msgParams.from) const address = normalizeAddress(msgParams.from)
return this.getKeyringForAccount(address) return this.getKeyringForAccount(address)
.then((keyring) => { .then((keyring) => {
return keyring.signMessage(address, msgParams.data) return keyring.signMessage(address, msgParams.data)
@ -392,8 +405,8 @@ module.exports = class KeyringController extends EventEmitter {
.then((accounts) => { .then((accounts) => {
const firstAccount = accounts[0] const firstAccount = accounts[0]
if (!firstAccount) throw new Error('KeyringController - No account found on keychain.') if (!firstAccount) throw new Error('KeyringController - No account found on keychain.')
const hexAccount = normalize(firstAccount) const hexAccount = normalizeAddress(firstAccount)
this.configManager.setSelectedAccount(hexAccount) this.setSelectedAccount(hexAccount)
this.emit('newAccount', hexAccount) this.emit('newAccount', hexAccount)
return this.setupAccounts(accounts) return this.setupAccounts(accounts)
}) })
@ -429,7 +442,7 @@ module.exports = class KeyringController extends EventEmitter {
if (!account) { if (!account) {
throw new Error('Problem loading account.') throw new Error('Problem loading account.')
} }
const address = normalize(account) const address = normalizeAddress(account)
this.ethStore.addAccount(address) this.ethStore.addAccount(address)
return this.createNickname(address) return this.createNickname(address)
} }
@ -441,10 +454,11 @@ module.exports = class KeyringController extends EventEmitter {
// //
// Takes an address, and assigns it an incremented nickname, persisting it. // Takes an address, and assigns it an incremented nickname, persisting it.
createNickname (address) { createNickname (address) {
const hexAddress = normalize(address) const hexAddress = normalizeAddress(address)
var i = Object.keys(this.identities).length const currentIdentityCount = Object.keys(this.identities).length + 1
const oldNickname = this.configManager.nicknameForWallet(address) const nicknames = this.store.getState().walletNicknames || {}
const name = oldNickname || `Account ${++i}` const existingNickname = nicknames[hexAddress]
const name = existingNickname || `Account ${currentIdentityCount}`
this.identities[hexAddress] = { this.identities[hexAddress] = {
address: hexAddress, address: hexAddress,
name, name,
@ -479,7 +493,7 @@ module.exports = class KeyringController extends EventEmitter {
return this.encryptor.encrypt(this.password, serializedKeyrings) return this.encryptor.encrypt(this.password, serializedKeyrings)
}) })
.then((encryptedString) => { .then((encryptedString) => {
this.configManager.setVault(encryptedString) this.store.updateState({ vault: encryptedString })
return true return true
}) })
} }
@ -492,7 +506,7 @@ module.exports = class KeyringController extends EventEmitter {
// Attempts to unlock the persisted encrypted storage, // Attempts to unlock the persisted encrypted storage,
// initializing the persisted keyrings to RAM. // initializing the persisted keyrings to RAM.
unlockKeyrings (password) { unlockKeyrings (password) {
const encryptedVault = this.configManager.getVault() const encryptedVault = this.store.getState().vault
if (!encryptedVault) { if (!encryptedVault) {
throw new Error('Cannot unlock without a previous vault.') throw new Error('Cannot unlock without a previous vault.')
} }
@ -572,7 +586,7 @@ module.exports = class KeyringController extends EventEmitter {
// Returns the currently initialized keyring that manages // Returns the currently initialized keyring that manages
// the specified `address` if one exists. // the specified `address` if one exists.
getKeyringForAccount (address) { getKeyringForAccount (address) {
const hexed = normalize(address) const hexed = normalizeAddress(address)
return Promise.all(this.keyrings.map((keyring) => { return Promise.all(this.keyrings.map((keyring) => {
return Promise.all([ return Promise.all([
@ -581,7 +595,7 @@ module.exports = class KeyringController extends EventEmitter {
]) ])
})) }))
.then(filter((candidate) => { .then(filter((candidate) => {
const accounts = candidate[1].map(normalize) const accounts = candidate[1].map(normalizeAddress)
return accounts.includes(hexed) return accounts.includes(hexed)
})) }))
.then((winners) => { .then((winners) => {
@ -639,10 +653,9 @@ module.exports = class KeyringController extends EventEmitter {
this.keyrings = [] this.keyrings = []
this.identities = {} this.identities = {}
this.configManager.setSelectedAccount() this.setSelectedAccount()
} }
} }
module.exports = KeyringController
function noop () {}

View File

@ -29,15 +29,7 @@ ConfigManager.prototype.setConfig = function (config) {
ConfigManager.prototype.getConfig = function () { ConfigManager.prototype.getConfig = function () {
var data = this.getData() var data = this.getData()
if ('config' in data) {
return data.config return data.config
} else {
return {
provider: {
type: 'testnet',
},
}
}
} }
ConfigManager.prototype.setRpcTarget = function (rpcUrl) { ConfigManager.prototype.setRpcTarget = function (rpcUrl) {

View File

@ -29,9 +29,10 @@ module.exports = class MetamaskController extends EventEmitter {
super() super()
this.opts = opts this.opts = opts
this.state = { network: 'loading' } this.state = { network: 'loading' }
let initState = opts.initState || {}
// observable state store // observable state store
this.store = new ObservableStore(opts.initState) this.store = new ObservableStore(initState)
// config manager // config manager
this.configManager = new ConfigManager({ this.configManager = new ConfigManager({
@ -41,7 +42,7 @@ module.exports = class MetamaskController extends EventEmitter {
// rpc provider // rpc provider
this.provider = this.initializeProvider(opts) this.provider = this.initializeProvider(opts)
this.provider.on('block', this.processBlock.bind(this)) this.provider.on('block', this.logBlock.bind(this))
this.provider.on('error', this.getNetwork.bind(this)) this.provider.on('error', this.getNetwork.bind(this))
// eth data query tools // eth data query tools
@ -50,6 +51,7 @@ module.exports = class MetamaskController extends EventEmitter {
// key mgmt // key mgmt
this.keyringController = new KeyringController({ this.keyringController = new KeyringController({
initState: initState.KeyringController,
ethStore: this.ethStore, ethStore: this.ethStore,
configManager: this.configManager, configManager: this.configManager,
getNetwork: this.getStateNetwork.bind(this), getNetwork: this.getStateNetwork.bind(this),
@ -96,6 +98,9 @@ module.exports = class MetamaskController extends EventEmitter {
this.ethStore.on('update', this.sendUpdate.bind(this)) this.ethStore.on('update', this.sendUpdate.bind(this))
this.keyringController.on('update', this.sendUpdate.bind(this)) this.keyringController.on('update', this.sendUpdate.bind(this))
this.txManager.on('update', this.sendUpdate.bind(this)) this.txManager.on('update', this.sendUpdate.bind(this))
this.keyringController.store.subscribe((state) => {
this.store.updateState({ KeyringController: state })
})
} }
// //
@ -142,9 +147,7 @@ module.exports = class MetamaskController extends EventEmitter {
const result = { selectedAccount: undefined } const result = { selectedAccount: undefined }
try { try {
result.selectedAccount = state.config.selectedAccount result.selectedAccount = state.config.selectedAccount
} catch (_) { } catch (_) {}
// thats fine, im sure it will be there next time...
}
return result return result
} }
@ -164,7 +167,9 @@ module.exports = class MetamaskController extends EventEmitter {
this.configManager.getConfig(), this.configManager.getConfig(),
this.txManager.getState(), this.txManager.getState(),
keyringControllerState, keyringControllerState,
this.noticeController.getState(), { this.noticeController.getState(),
{
shapeShiftTxList: this.configManager.getShapeShiftTxList(),
lostAccounts: this.configManager.getLostAccounts(), lostAccounts: this.configManager.getLostAccounts(),
} }
) )
@ -269,6 +274,13 @@ module.exports = class MetamaskController extends EventEmitter {
} }
} }
setupPublicConfig (outStream) {
pipe(
this.publicConfigStore,
outStream
)
}
sendUpdate () { sendUpdate () {
this.getState() this.getState()
.then((state) => { .then((state) => {
@ -374,163 +386,6 @@ module.exports = class MetamaskController extends EventEmitter {
this.opts.showUnconfirmedMessage(msgParams, msgId) this.opts.showUnconfirmedMessage(msgParams, msgId)
} }
setupPublicConfig (outStream) {
pipe(
this.publicConfigStore,
outStream
)
}
// Log blocks
processBlock (block) {
if (global.METAMASK_DEBUG) {
console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
}
this.verifyNetwork()
}
verifyNetwork () {
// Check network when restoring connectivity:
if (this.state.network === 'loading') {
this.getNetwork()
}
}
// config
//
setTOSHash (hash) {
try {
this.configManager.setTOSHash(hash)
} catch (err) {
console.error('Error in setting terms of service hash.')
}
}
checkTOSChange () {
try {
const storedHash = this.configManager.getTOSHash() || 0
if (storedHash !== global.TOS_HASH) {
this.resetDisclaimer()
this.setTOSHash(global.TOS_HASH)
}
} catch (err) {
console.error('Error in checking TOS change.')
}
}
// disclaimer
agreeToDisclaimer (cb) {
try {
this.configManager.setConfirmedDisclaimer(true)
cb()
} catch (err) {
cb(err)
}
}
resetDisclaimer () {
try {
this.configManager.setConfirmedDisclaimer(false)
} catch (e) {
console.error(e)
}
}
setCurrentFiat (fiat, cb) {
try {
this.configManager.setCurrentFiat(fiat)
this.configManager.updateConversionRate()
this.scheduleConversionInterval()
const data = {
conversionRate: this.configManager.getConversionRate(),
currentFiat: this.configManager.getCurrentFiat(),
conversionDate: this.configManager.getConversionDate(),
}
cb(data)
} catch (err) {
cb(null, err)
}
}
scheduleConversionInterval () {
if (this.conversionInterval) {
clearInterval(this.conversionInterval)
}
this.conversionInterval = setInterval(() => {
this.configManager.updateConversionRate()
}, 300000)
}
// called from popup
setRpcTarget (rpcTarget) {
this.configManager.setRpcTarget(rpcTarget)
extension.runtime.reload()
this.getNetwork()
}
setProviderType (type) {
this.configManager.setProviderType(type)
extension.runtime.reload()
this.getNetwork()
}
useEtherscanProvider () {
this.configManager.useEtherscanProvider()
extension.runtime.reload()
}
buyEth (address, amount) {
if (!amount) amount = '5'
var network = this.state.network
var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
if (network === '3') {
url = 'https://faucet.metamask.io/'
}
extension.tabs.create({
url,
})
}
createShapeShiftTx (depositAddress, depositType) {
this.configManager.createShapeShiftTx(depositAddress, depositType)
}
getNetwork (err) {
if (err) {
this.state.network = 'loading'
this.sendUpdate()
}
this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) {
this.state.network = 'loading'
return this.sendUpdate()
}
if (global.METAMASK_DEBUG) {
console.log('web3.getNetwork returned ' + network)
}
this.state.network = network
this.sendUpdate()
})
}
setGasMultiplier (gasMultiplier, cb) {
try {
this.configManager.setGasMultiplier(gasMultiplier)
cb()
} catch (err) {
cb(err)
}
}
getStateNetwork () {
return this.state.network
}
markAccountsFound (cb) { markAccountsFound (cb) {
this.configManager.setLostAccounts([]) this.configManager.setLostAccounts([])
@ -593,4 +448,162 @@ module.exports = class MetamaskController extends EventEmitter {
data: privKeys, data: privKeys,
}) })
} }
//
// disclaimer
//
agreeToDisclaimer (cb) {
try {
this.configManager.setConfirmedDisclaimer(true)
cb()
} catch (err) {
cb(err)
}
}
resetDisclaimer () {
try {
this.configManager.setConfirmedDisclaimer(false)
} catch (e) {
console.error(e)
}
}
setTOSHash (hash) {
try {
this.configManager.setTOSHash(hash)
} catch (err) {
console.error('Error in setting terms of service hash.')
}
}
checkTOSChange () {
try {
const storedHash = this.configManager.getTOSHash() || 0
if (storedHash !== global.TOS_HASH) {
this.resetDisclaimer()
this.setTOSHash(global.TOS_HASH)
}
} catch (err) {
console.error('Error in checking TOS change.')
}
}
//
// config
//
// Log blocks
logBlock (block) {
if (global.METAMASK_DEBUG) {
console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
}
this.verifyNetwork()
}
setCurrentFiat (fiat, cb) {
try {
this.configManager.setCurrentFiat(fiat)
this.configManager.updateConversionRate()
this.scheduleConversionInterval()
const data = {
conversionRate: this.configManager.getConversionRate(),
currentFiat: this.configManager.getCurrentFiat(),
conversionDate: this.configManager.getConversionDate(),
}
cb(data)
} catch (err) {
cb(null, err)
}
}
scheduleConversionInterval () {
if (this.conversionInterval) {
clearInterval(this.conversionInterval)
}
this.conversionInterval = setInterval(() => {
this.configManager.updateConversionRate()
}, 300000)
}
buyEth (address, amount) {
if (!amount) amount = '5'
var network = this.state.network
var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
if (network === '3') {
url = 'https://faucet.metamask.io/'
}
extension.tabs.create({
url,
})
}
createShapeShiftTx (depositAddress, depositType) {
this.configManager.createShapeShiftTx(depositAddress, depositType)
}
setGasMultiplier (gasMultiplier, cb) {
try {
this.configManager.setGasMultiplier(gasMultiplier)
cb()
} catch (err) {
cb(err)
}
}
//
// network
//
verifyNetwork () {
// Check network when restoring connectivity:
if (this.state.network === 'loading') {
this.getNetwork()
}
}
setRpcTarget (rpcTarget) {
this.configManager.setRpcTarget(rpcTarget)
extension.runtime.reload()
this.getNetwork()
}
setProviderType (type) {
this.configManager.setProviderType(type)
extension.runtime.reload()
this.getNetwork()
}
useEtherscanProvider () {
this.configManager.useEtherscanProvider()
extension.runtime.reload()
}
getStateNetwork () {
return this.state.network
}
getNetwork (err) {
if (err) {
this.state.network = 'loading'
this.sendUpdate()
}
this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) {
this.state.network = 'loading'
return this.sendUpdate()
}
if (global.METAMASK_DEBUG) {
console.log('web3.getNetwork returned ' + network)
}
this.state.network = network
this.sendUpdate()
})
}
} }

View File

@ -0,0 +1,41 @@
const version = 5
/*
This migration moves state from the flat state trie into KeyringController substate
*/
const extend = require('xtend')
module.exports = {
version,
migrate: function (versionedData) {
versionedData.meta.version = version
try {
const state = versionedData.data
const newState = selectSubstateForKeyringController(state)
versionedData.data = newState
} catch (err) {
console.warn('MetaMask Migration #5' + err.stack)
}
return Promise.resolve(versionedData)
},
}
function selectSubstateForKeyringController (state) {
const config = state.config
const newState = extend(state, {
KeyringController: {
vault: state.vault,
selectedAccount: config.selectedAccount,
walletNicknames: state.walletNicknames,
},
})
delete newState.vault
delete newState.walletNicknames
delete newState.config.selectedAccount
return newState
}

View File

@ -15,4 +15,5 @@ module.exports = [
require('./002'), require('./002'),
require('./003'), require('./003'),
require('./004'), require('./004'),
require('./005'),
] ]

View File

@ -70,7 +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", "obs-store": "^2.3.0",
"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",

View File

@ -4,12 +4,13 @@ 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')
const firstTimeState = require('../../app/scripts/first-time-state')
const delegateCallCode = require('../lib/example-code.json').delegateCallCode const delegateCallCode = require('../lib/example-code.json').delegateCallCode
const clone = require('clone')
// The old way: // The old way:
const IdentityStore = require('../../app/scripts/lib/idStore') const IdentityStore = require('../../app/scripts/lib/idStore')
const STORAGE_KEY = 'metamask-config' const STORAGE_KEY = 'metamask-config'
const extend = require('xtend')
// The new ways: // The new ways:
var KeyringController = require('../../app/scripts/keyring-controller') var KeyringController = require('../../app/scripts/keyring-controller')
@ -42,12 +43,9 @@ describe('IdentityStore to KeyringController migration', function() {
// and THEN create a new one, before we can run tests on it. // and THEN create a new one, before we can run tests on it.
beforeEach(function(done) { beforeEach(function(done) {
this.sinon = sinon.sandbox.create() this.sinon = sinon.sandbox.create()
window.localStorage = {} // Hacking localStorage support into JSDom let store = new ObservableStore(clone(firstTimeState))
let store = new ObservableStore(loadData())
store.subscribe(setData)
configManager = new ConfigManager({ store }) configManager = new ConfigManager({ store })
idStore = new IdentityStore({ idStore = new IdentityStore({
configManager: configManager, configManager: configManager,
ethStore: { ethStore: {
@ -91,56 +89,9 @@ describe('IdentityStore to KeyringController migration', function() {
assert(!state.lostAccounts, 'no lost accounts') assert(!state.lostAccounts, 'no lost accounts')
done() done()
}) })
.catch((err) => {
done(err)
})
}) })
}) })
}) })
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 },
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

@ -1,6 +1,6 @@
var assert = require('assert') const assert = require('assert')
var KeyringController = require('../../app/scripts/keyring-controller') const KeyringController = require('../../app/scripts/keyring-controller')
var configManagerGen = require('../lib/mock-config-manager') const configManagerGen = require('../lib/mock-config-manager')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
const async = require('async') const async = require('async')
@ -55,17 +55,16 @@ describe('KeyringController', function() {
this.timeout(10000) this.timeout(10000)
it('should set a vault on the configManager', function(done) { it('should set a vault on the configManager', function(done) {
keyringController.configManager.setVault(null) keyringController.store.updateState({ vault: null })
assert(!keyringController.configManager.getVault(), 'no previous vault') assert(!keyringController.store.getState().vault, 'no previous vault')
keyringController.createNewVaultAndKeychain(password) keyringController.createNewVaultAndKeychain(password)
.then(() => { .then(() => {
const vault = keyringController.configManager.getVault() const vault = keyringController.store.getState().vault
assert(vault, 'vault created') assert(vault, 'vault created')
done() done()
}) })
.catch((reason) => { .catch((reason) => {
assert.ifError(reason) done(reason)
done()
}) })
}) })
}) })
@ -96,8 +95,7 @@ describe('KeyringController', function() {
done() done()
}) })
.catch((reason) => { .catch((reason) => {
assert.ifError(reason) done(reason)
done()
}) })
}) })
}) })
@ -109,9 +107,6 @@ describe('KeyringController', function() {
const identities = keyringController.identities const identities = keyringController.identities
const identity = identities[fakeAddress] const identity = identities[fakeAddress]
assert.equal(identity.address, fakeAddress) assert.equal(identity.address, fakeAddress)
const nick = keyringController.configManager.nicknameForWallet(fakeAddress)
assert.equal(typeof nick, 'string')
}) })
}) })
@ -122,34 +117,17 @@ describe('KeyringController', function() {
keyringController.identities[ethUtil.addHexPrefix(account)] = {} keyringController.identities[ethUtil.addHexPrefix(account)] = {}
keyringController.saveAccountLabel(account, nick) keyringController.saveAccountLabel(account, nick)
.then((label) => { .then((label) => {
try {
assert.equal(label, nick) assert.equal(label, nick)
const persisted = keyringController.configManager.nicknameForWallet(account) const persisted = keyringController.store.getState().walletNicknames[account]
assert.equal(persisted, nick) assert.equal(persisted, nick)
done() done()
} catch (err) {
done()
}
}) })
.catch((reason) => { .catch((reason) => {
assert.ifError(reason) done(reason)
done()
})
})
this.timeout(10000)
it('retrieves the persisted nickname', function(done) {
const account = addresses[0]
var nick = 'Test nickname'
keyringController.configManager.setNicknameForWallet(account, nick)
keyringController.createNewVaultAndRestore(password, seedWords)
.then((state) => {
const identity = keyringController.identities['0x' + account]
assert.equal(identity.name, nick)
assert(accounts)
done()
})
.catch((reason) => {
assert.ifError(reason)
done()
}) })
}) })
}) })

View File

@ -1,7 +1,9 @@
var assert = require('assert') const assert = require('assert')
var MetaMaskController = require('../../app/scripts/metamask-controller') const sinon = require('sinon')
var sinon = require('sinon') const clone = require('clone')
var extend = require('xtend') const MetaMaskController = require('../../app/scripts/metamask-controller')
const firstTimeState = require('../../app/scripts/first-time-state')
const STORAGE_KEY = 'metamask-config' const STORAGE_KEY = 'metamask-config'
describe('MetaMaskController', function() { describe('MetaMaskController', function() {
@ -11,15 +13,12 @@ describe('MetaMaskController', function() {
unlockAccountMessage: noop, unlockAccountMessage: noop,
showUnapprovedTx: noop, showUnapprovedTx: noop,
// initial state // initial state
initState: loadData(), initState: clone(firstTimeState),
}) })
// 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
this.sinon = sinon.sandbox.create() this.sinon = sinon.sandbox.create()
window.localStorage = {} // Hacking localStorage support into JSDom
}) })
afterEach(function() { afterEach(function() {
@ -28,54 +27,3 @@ describe('MetaMaskController', function() {
}) })
}) })
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)
}

View File

@ -41,6 +41,7 @@ function AccountDetailScreen () {
AccountDetailScreen.prototype.render = function () { AccountDetailScreen.prototype.render = function () {
var props = this.props var props = this.props
var selected = props.address || Object.keys(props.accounts)[0] var selected = props.address || Object.keys(props.accounts)[0]
var checksumAddress = selected && ethUtil.toChecksumAddress(selected)
var identity = props.identities[selected] var identity = props.identities[selected]
var account = props.accounts[selected] var account = props.accounts[selected]
const { network } = props const { network } = props
@ -116,7 +117,7 @@ AccountDetailScreen.prototype.render = function () {
marginBottom: '15px', marginBottom: '15px',
color: '#AEAEAE', color: '#AEAEAE',
}, },
}, ethUtil.toChecksumAddress(selected)), }, checksumAddress),
// copy and export // copy and export
@ -129,7 +130,7 @@ AccountDetailScreen.prototype.render = function () {
h(AccountInfoLink, { selected, network }), h(AccountInfoLink, { selected, network }),
h(CopyButton, { h(CopyButton, {
value: ethUtil.toChecksumAddress(selected), value: checksumAddress,
}), }),
h(Tooltip, { h(Tooltip, {

View File

@ -17,6 +17,7 @@ function AccountListItem () {
AccountListItem.prototype.render = function () { AccountListItem.prototype.render = function () {
const { identity, selectedAccount, accounts, onShowDetail } = this.props const { identity, selectedAccount, accounts, onShowDetail } = this.props
const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address)
const isSelected = selectedAccount === identity.address const isSelected = selectedAccount === identity.address
const account = accounts[identity.address] const account = accounts[identity.address]
const selectedClass = isSelected ? '.selected' : '' const selectedClass = isSelected ? '.selected' : ''
@ -48,7 +49,7 @@ AccountListItem.prototype.render = function () {
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
}, },
}, ethUtil.toChecksumAddress(identity.address)), }, checksumAddress),
h(EthBalance, { h(EthBalance, {
value: account && account.balance, value: account && account.balance,
style: { style: {
@ -65,7 +66,7 @@ AccountListItem.prototype.render = function () {
}, },
}, [ }, [
h(CopyButton, { h(CopyButton, {
value: ethUtil.toChecksumAddress(identity.address), value: checksumAddress,
}), }),
]), ]),
]) ])

View File

@ -152,7 +152,6 @@ InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
var password = passwordBox.value var password = passwordBox.value
var passwordConfirmBox = document.getElementById('password-box-confirm') var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value var passwordConfirm = passwordConfirmBox.value
// var entropy = document.getElementById('entropy-text-entry').value
if (password.length < 8) { if (password.length < 8) {
this.warning = 'password not long enough' this.warning = 'password not long enough'