diff options
author | Frankie <frankie.diamond@gmail.com> | 2017-01-29 14:59:28 +0800 |
---|---|---|
committer | Frankie <frankie.diamond@gmail.com> | 2017-01-29 14:59:28 +0800 |
commit | fdee1e3701a84fe1321de348ec2d11bf5361a62d (patch) | |
tree | 1844d6c25cc824c9303ab79103525cab08d5e0b3 /app | |
parent | 8be68575bbef1dcc89b51355abaee90dbf018387 (diff) | |
parent | 47b48c0e02b2b9508813abfda0d89a1bbe677694 (diff) | |
download | tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.tar tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.tar.gz tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.tar.bz2 tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.tar.lz tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.tar.xz tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.tar.zst tangerine-wallet-browser-fdee1e3701a84fe1321de348ec2d11bf5361a62d.zip |
Merge branch 'dev' into messageManagerCleanUp
Diffstat (limited to 'app')
-rw-r--r-- | app/scripts/keyring-controller.js | 101 | ||||
-rw-r--r-- | app/scripts/lib/config-manager.js | 10 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 257 | ||||
-rw-r--r-- | app/scripts/migrations/005.js | 41 | ||||
-rw-r--r-- | app/scripts/migrations/index.js | 1 |
5 files changed, 236 insertions, 174 deletions
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index b6c9aa3a7..b56b5520f 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -1,12 +1,12 @@ const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN const bip39 = require('bip39') const EventEmitter = require('events').EventEmitter +const ObservableStore = require('obs-store') const filter = require('promise-filter') const encryptor = require('browser-passworder') - -const normalize = require('./lib/sig-util').normalize -const BN = ethUtil.BN - +const normalizeAddress = require('./lib/sig-util').normalize +function noop () {} // Keyrings: const SimpleKeyring = require('./keyrings/simple') const HdKeyring = require('./keyrings/hd') @@ -15,7 +15,7 @@ const keyringTypes = [ HdKeyring, ] -module.exports = class KeyringController extends EventEmitter { +class KeyringController extends EventEmitter { // PUBLIC METHODS // @@ -26,6 +26,8 @@ module.exports = class KeyringController extends EventEmitter { constructor (opts) { super() + const initState = opts.initState || {} + this.store = new ObservableStore(initState) this.configManager = opts.configManager this.ethStore = opts.ethStore this.encryptor = encryptor @@ -65,27 +67,29 @@ module.exports = class KeyringController extends EventEmitter { // in this class, but will need to be Promisified when we move our // persistence to an async model. getState () { - const configManager = this.configManager - 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)) + return Promise.all(this.keyrings.map(this.displayForKeyring)) .then((displayKeyrings) => { + const state = this.store.getState() + // old wallet + const wallet = this.configManager.getWallet() 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(), - isInitialized: (!!wallet || !!vault), - isUnlocked: Boolean(this.password), isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), - selectedAccount: address, - shapeShiftTxList: this.configManager.getShapeShiftTxList(), currentFiat: this.configManager.getCurrentFiat(), conversionRate: this.configManager.getConversionRate(), conversionDate: this.configManager.getConversionDate(), - keyringTypes: this.keyringTypes.map(krt => krt.type), - identities: this.identities, - keyrings: displayKeyrings, + // messageManager } }) } @@ -138,8 +142,8 @@ module.exports = class KeyringController extends EventEmitter { .then((accounts) => { const firstAccount = accounts[0] if (!firstAccount) throw new Error('KeyringController - First Account not found.') - const hexAccount = normalize(firstAccount) - this.configManager.setSelectedAccount(hexAccount) + const hexAccount = normalizeAddress(firstAccount) + this.setSelectedAccount(hexAccount) return this.setupAccounts(accounts) }) .then(this.persistAllKeyrings.bind(this, password)) @@ -236,9 +240,9 @@ module.exports = class KeyringController extends EventEmitter { // // Sets the state's `selectedAccount` value // to the specified address. - setSelectedAccount (address) { - var addr = normalize(address) - this.configManager.setSelectedAccount(addr) + setSelectedAccount (account) { + var address = normalizeAddress(account) + this.store.updateState({ selectedAccount: address }) return this.fullUpdate() } @@ -250,11 +254,19 @@ module.exports = class KeyringController extends EventEmitter { // // Persists a nickname equal to `label` for the specified account. saveAccountLabel (account, label) { - const address = normalize(account) - const configManager = this.configManager - configManager.setNicknameForWallet(address, label) - this.identities[address].name = label - return Promise.resolve(label) + try { + const hexAddress = normalizeAddress(account) + // update state on diskStore + 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) + } catch (err) { + return Promise.reject(err) + } } // Export Account @@ -270,7 +282,7 @@ module.exports = class KeyringController extends EventEmitter { try { return this.getKeyringForAccount(address) .then((keyring) => { - return keyring.exportAccount(normalize(address)) + return keyring.exportAccount(normalizeAddress(address)) }) } catch (e) { return Promise.reject(e) @@ -284,7 +296,7 @@ module.exports = class KeyringController extends EventEmitter { // TX Manager to update the state after signing signTransaction (ethTx, _fromAddress) { - const fromAddress = normalize(_fromAddress) + const fromAddress = normalizeAddress(_fromAddress) return this.getKeyringForAccount(fromAddress) .then((keyring) => { return keyring.signTransaction(fromAddress, ethTx) @@ -328,8 +340,8 @@ module.exports = class KeyringController extends EventEmitter { .then((accounts) => { const firstAccount = accounts[0] if (!firstAccount) throw new Error('KeyringController - No account found on keychain.') - const hexAccount = normalize(firstAccount) - this.configManager.setSelectedAccount(hexAccount) + const hexAccount = normalizeAddress(firstAccount) + this.setSelectedAccount(hexAccount) this.emit('newAccount', hexAccount) return this.setupAccounts(accounts) }) @@ -365,7 +377,7 @@ module.exports = class KeyringController extends EventEmitter { if (!account) { throw new Error('Problem loading account.') } - const address = normalize(account) + const address = normalizeAddress(account) this.ethStore.addAccount(address) return this.createNickname(address) } @@ -377,10 +389,11 @@ module.exports = class KeyringController extends EventEmitter { // // Takes an address, and assigns it an incremented nickname, persisting it. createNickname (address) { - const hexAddress = normalize(address) - var i = Object.keys(this.identities).length - const oldNickname = this.configManager.nicknameForWallet(address) - const name = oldNickname || `Account ${++i}` + const hexAddress = normalizeAddress(address) + const currentIdentityCount = Object.keys(this.identities).length + 1 + const nicknames = this.store.getState().walletNicknames || {} + const existingNickname = nicknames[hexAddress] + const name = existingNickname || `Account ${currentIdentityCount}` this.identities[hexAddress] = { address: hexAddress, name, @@ -415,7 +428,7 @@ module.exports = class KeyringController extends EventEmitter { return this.encryptor.encrypt(this.password, serializedKeyrings) }) .then((encryptedString) => { - this.configManager.setVault(encryptedString) + this.store.updateState({ vault: encryptedString }) return true }) } @@ -428,7 +441,7 @@ module.exports = class KeyringController extends EventEmitter { // Attempts to unlock the persisted encrypted storage, // initializing the persisted keyrings to RAM. unlockKeyrings (password) { - const encryptedVault = this.configManager.getVault() + const encryptedVault = this.store.getState().vault if (!encryptedVault) { throw new Error('Cannot unlock without a previous vault.') } @@ -508,7 +521,7 @@ module.exports = class KeyringController extends EventEmitter { // Returns the currently initialized keyring that manages // the specified `address` if one exists. getKeyringForAccount (address) { - const hexed = normalize(address) + const hexed = normalizeAddress(address) return Promise.all(this.keyrings.map((keyring) => { return Promise.all([ @@ -517,7 +530,7 @@ module.exports = class KeyringController extends EventEmitter { ]) })) .then(filter((candidate) => { - const accounts = candidate[1].map(normalize) + const accounts = candidate[1].map(normalizeAddress) return accounts.includes(hexed) })) .then((winners) => { @@ -575,7 +588,9 @@ module.exports = class KeyringController extends EventEmitter { this.keyrings = [] this.identities = {} - this.configManager.setSelectedAccount() + this.setSelectedAccount() } } + +module.exports = KeyringController diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index fd4ac511a..357e081b1 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -29,15 +29,7 @@ ConfigManager.prototype.setConfig = function (config) { ConfigManager.prototype.getConfig = function () { var data = this.getData() - if ('config' in data) { - return data.config - } else { - return { - provider: { - type: 'testnet', - }, - } - } + return data.config } ConfigManager.prototype.setRpcTarget = function (rpcUrl) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 571968b65..a76d5cd86 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -29,9 +29,10 @@ module.exports = class MetamaskController extends EventEmitter { super() this.opts = opts this.state = { network: 'loading' } + let initState = opts.initState || {} // observable state store - this.store = new ObservableStore(opts.initState) + this.store = new ObservableStore(initState) // config manager this.configManager = new ConfigManager({ @@ -41,7 +42,7 @@ module.exports = class MetamaskController extends EventEmitter { // rpc provider 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)) // eth data query tools @@ -50,6 +51,7 @@ module.exports = class MetamaskController extends EventEmitter { // key mgmt this.keyringController = new KeyringController({ + initState: initState.KeyringController, ethStore: this.ethStore, configManager: this.configManager, getNetwork: this.getStateNetwork.bind(this), @@ -97,6 +99,9 @@ module.exports = class MetamaskController extends EventEmitter { this.keyringController.on('update', this.sendUpdate.bind(this)) this.txManager.on('update', this.sendUpdate.bind(this)) this.messageManager.on('update', this.sendUpdate.bind(this)) + this.keyringController.store.subscribe((state) => { + this.store.updateState({ KeyringController: state }) + }) } // @@ -139,9 +144,7 @@ module.exports = class MetamaskController extends EventEmitter { const result = { selectedAccount: undefined } try { result.selectedAccount = state.config.selectedAccount - } catch (_) { - // thats fine, im sure it will be there next time... - } + } catch (_) {} return result } @@ -162,7 +165,9 @@ module.exports = class MetamaskController extends EventEmitter { this.txManager.getState(), this.messageManager.getState(), keyringControllerState, - this.noticeController.getState(), { + this.noticeController.getState(), + { + shapeShiftTxList: this.configManager.getShapeShiftTxList(), lostAccounts: this.configManager.getLostAccounts(), } ) @@ -268,6 +273,13 @@ module.exports = class MetamaskController extends EventEmitter { } } + setupPublicConfig (outStream) { + pipe( + this.publicConfigStore, + outStream + ) + } + sendUpdate () { this.getState() .then((state) => { @@ -388,31 +400,90 @@ module.exports = class MetamaskController extends EventEmitter { }).catch((err) => cb(err)) } - setupPublicConfig (outStream) { - pipe( - this.publicConfigStore, - outStream - ) + + markAccountsFound (cb) { + this.configManager.setLostAccounts([]) + this.sendUpdate() + cb(null, this.getState()) } - // Log blocks - processBlock (block) { - if (global.METAMASK_DEBUG) { - console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`) + // Migrate Old Vault If Any + // @string password + // + // returns Promise() + // + // Temporary step used when logging in. + // Checks if old style (pre-3.0.0) Metamask Vault exists. + // If so, persists that vault in the new vault format + // with the provided password, so the other unlock steps + // may be completed without interruption. + migrateOldVaultIfAny (password) { + + if (!this.checkIfShouldMigrate()) { + return Promise.resolve(password) } - this.verifyNetwork() + + const keyringController = this.keyringController + + return this.idStoreMigrator.migratedVaultForPassword(password) + .then(this.restoreOldVaultAccounts.bind(this)) + .then(this.restoreOldLostAccounts.bind(this)) + .then(keyringController.persistAllKeyrings.bind(keyringController, password)) + .then(() => password) } - verifyNetwork () { - // Check network when restoring connectivity: - if (this.state.network === 'loading') { - this.getNetwork() + checkIfShouldMigrate() { + return !!this.configManager.getWallet() && !this.configManager.getVault() + } + + restoreOldVaultAccounts(migratorOutput) { + const { serialized } = migratorOutput + return this.keyringController.restoreKeyring(serialized) + .then(() => migratorOutput) + } + + restoreOldLostAccounts(migratorOutput) { + const { lostAccounts } = migratorOutput + if (lostAccounts) { + this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) + return this.importLostAccounts(migratorOutput) } + return Promise.resolve(migratorOutput) } - // config + // IMPORT LOST ACCOUNTS + // @Object with key lostAccounts: @Array accounts <{ address, privateKey }> + // Uses the array's private keys to create a new Simple Key Pair keychain + // and add it to the keyring controller. + importLostAccounts ({ lostAccounts }) { + const privKeys = lostAccounts.map(acct => acct.privateKey) + return this.keyringController.restoreKeyring({ + type: 'Simple Key Pair', + 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) @@ -433,23 +504,16 @@ module.exports = class MetamaskController extends EventEmitter { } } - // disclaimer - - agreeToDisclaimer (cb) { - try { - this.configManager.setConfirmedDisclaimer(true) - cb() - } catch (err) { - cb(err) - } - } + // + // config + // - resetDisclaimer () { - try { - this.configManager.setConfirmedDisclaimer(false) - } catch (e) { - console.error(e) + // 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) { @@ -477,24 +541,6 @@ module.exports = class MetamaskController extends EventEmitter { }, 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' @@ -514,25 +560,6 @@ module.exports = class MetamaskController extends EventEmitter { 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) @@ -542,69 +569,55 @@ module.exports = class MetamaskController extends EventEmitter { } } - getStateNetwork () { - return this.state.network - } - - markAccountsFound (cb) { - this.configManager.setLostAccounts([]) - this.sendUpdate() - cb(null, this.getState()) - } - - // Migrate Old Vault If Any - // @string password // - // returns Promise() + // network // - // Temporary step used when logging in. - // Checks if old style (pre-3.0.0) Metamask Vault exists. - // If so, persists that vault in the new vault format - // with the provided password, so the other unlock steps - // may be completed without interruption. - migrateOldVaultIfAny (password) { - if (!this.checkIfShouldMigrate()) { - return Promise.resolve(password) + verifyNetwork () { + // Check network when restoring connectivity: + if (this.state.network === 'loading') { + this.getNetwork() } + } - const keyringController = this.keyringController + setRpcTarget (rpcTarget) { + this.configManager.setRpcTarget(rpcTarget) + extension.runtime.reload() + this.getNetwork() + } - return this.idStoreMigrator.migratedVaultForPassword(password) - .then(this.restoreOldVaultAccounts.bind(this)) - .then(this.restoreOldLostAccounts.bind(this)) - .then(keyringController.persistAllKeyrings.bind(keyringController, password)) - .then(() => password) + setProviderType (type) { + this.configManager.setProviderType(type) + extension.runtime.reload() + this.getNetwork() } - checkIfShouldMigrate() { - return !!this.configManager.getWallet() && !this.configManager.getVault() + useEtherscanProvider () { + this.configManager.useEtherscanProvider() + extension.runtime.reload() } - restoreOldVaultAccounts(migratorOutput) { - const { serialized } = migratorOutput - return this.keyringController.restoreKeyring(serialized) - .then(() => migratorOutput) + getStateNetwork () { + return this.state.network } - restoreOldLostAccounts(migratorOutput) { - const { lostAccounts } = migratorOutput - if (lostAccounts) { - this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address)) - return this.importLostAccounts(migratorOutput) + getNetwork (err) { + if (err) { + this.state.network = 'loading' + this.sendUpdate() } - return Promise.resolve(migratorOutput) - } - // IMPORT LOST ACCOUNTS - // @Object with key lostAccounts: @Array accounts <{ address, privateKey }> - // Uses the array's private keys to create a new Simple Key Pair keychain - // and add it to the keyring controller. - importLostAccounts ({ lostAccounts }) { - const privKeys = lostAccounts.map(acct => acct.privateKey) - return this.keyringController.restoreKeyring({ - type: 'Simple Key Pair', - data: privKeys, + 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() }) } + } diff --git a/app/scripts/migrations/005.js b/app/scripts/migrations/005.js new file mode 100644 index 000000000..65f62a861 --- /dev/null +++ b/app/scripts/migrations/005.js @@ -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 +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index d2ac221b9..a7ce745e7 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -15,4 +15,5 @@ module.exports = [ require('./002'), require('./003'), require('./004'), + require('./005'), ] |