aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorFrankie <frankie.diamond@gmail.com>2016-12-22 06:31:07 +0800
committerGitHub <noreply@github.com>2016-12-22 06:31:07 +0800
commit6f7c23fd28ef37ae51478b735c1f5888c97bbaf5 (patch)
treeb927871ed00c33a66341a26de09f00cf006314b8 /app
parenta85c691b71d5142d2412000930328fbe9161760a (diff)
parent73cdf0bfd49470bad1f0da4d0d894278c87af54e (diff)
downloadtangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.tar
tangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.tar.gz
tangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.tar.bz2
tangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.tar.lz
tangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.tar.xz
tangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.tar.zst
tangerine-wallet-browser-6f7c23fd28ef37ae51478b735c1f5888c97bbaf5.zip
Merge branch 'dev' into TxManager
Diffstat (limited to 'app')
-rw-r--r--app/manifest.json2
-rw-r--r--app/scripts/config.js3
-rw-r--r--app/scripts/inpage.js18
-rw-r--r--app/scripts/keyring-controller.js14
-rw-r--r--app/scripts/keyrings/hd.js8
-rw-r--r--app/scripts/lib/config-manager.js11
-rw-r--r--app/scripts/lib/encryptor.js156
-rw-r--r--app/scripts/lib/idStore-migrator.js43
-rw-r--r--app/scripts/metamask-controller.js32
-rw-r--r--app/scripts/notice-controller.js96
10 files changed, 191 insertions, 192 deletions
diff --git a/app/manifest.json b/app/manifest.json
index b9d3735ad..61775ed93 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
- "version": "2.13.10",
+ "version": "2.14.1",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "Ethereum Browser Extension",
diff --git a/app/scripts/config.js b/app/scripts/config.js
index 9a0f2a50b..e09206c5f 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -1,6 +1,5 @@
const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
const TESTNET_RPC_URL = 'https://ropsten.infura.io/metamask'
-const MORDEN_RPC_URL = 'https://morden.infura.io/metamask'
const DEFAULT_RPC_URL = TESTNET_RPC_URL
global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
@@ -11,6 +10,6 @@ module.exports = {
default: DEFAULT_RPC_URL,
mainnet: MAINET_RPC_URL,
testnet: TESTNET_RPC_URL,
- morden: MORDEN_RPC_URL,
+ morden: TESTNET_RPC_URL,
},
}
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 68c6165c8..42332d92e 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -2,8 +2,8 @@
cleanContextForImports()
require('web3/dist/web3.min.js')
const LocalMessageDuplexStream = require('post-message-stream')
-const PingStream = require('ping-pong-stream/ping')
-const endOfStream = require('end-of-stream')
+// const PingStream = require('ping-pong-stream/ping')
+// const endOfStream = require('end-of-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
@@ -40,13 +40,15 @@ reloadStream.once('data', triggerReload)
// setup ping timeout autoreload
// LocalMessageDuplexStream does not self-close, so reload if pingStream fails
-var pingChannel = inpageProvider.multiStream.createStream('pingpong')
-var pingStream = new PingStream({ objectMode: true })
+// var pingChannel = inpageProvider.multiStream.createStream('pingpong')
+// var pingStream = new PingStream({ objectMode: true })
// wait for first successful reponse
-metamaskStream.once('_data', function () {
- pingStream.pipe(pingChannel).pipe(pingStream)
-})
-endOfStream(pingStream, triggerReload)
+
+// disable pingStream until https://github.com/MetaMask/metamask-plugin/issues/746 is resolved more gracefully
+// metamaskStream.once('data', function(){
+// pingStream.pipe(pingChannel).pipe(pingStream)
+// })
+// endOfStream(pingStream, triggerReload)
// set web3 defaultAcount
inpageProvider.publicConfigStore.subscribe(function (state) {
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
index a58742228..6623419df 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -2,8 +2,9 @@ const ethUtil = require('ethereumjs-util')
const bip39 = require('bip39')
const EventEmitter = require('events').EventEmitter
const filter = require('promise-filter')
+const encryptor = require('browser-passworder')
+
const normalize = require('./lib/sig-util').normalize
-const encryptor = require('./lib/encryptor')
const messageManager = require('./lib/message-manager')
const IdStoreMigrator = require('./lib/idStore-migrator')
const BN = ethUtil.BN
@@ -106,6 +107,7 @@ module.exports = class KeyringController extends EventEmitter {
conversionDate: this.configManager.getConversionDate(),
keyringTypes: this.keyringTypes.map(krt => krt.type),
identities: this.identities,
+ lostAccounts: this.configManager.getLostAccounts(),
}
}
@@ -430,11 +432,17 @@ module.exports = class KeyringController extends EventEmitter {
// may be completed without interruption.
migrateOldVaultIfAny (password) {
const shouldMigrate = !!this.configManager.getWallet() && !this.configManager.getVault()
+ if (!shouldMigrate) {
+ return Promise.resolve()
+ }
+
return this.idStoreMigrator.migratedVaultForPassword(password)
- .then((serialized) => {
+ .then((result) => {
this.password = password
- if (serialized && shouldMigrate) {
+ if (result && shouldMigrate) {
+ const { serialized, lostAccounts } = result
+ this.configManager.setLostAccounts(lostAccounts)
return this.restoreKeyring(serialized)
.then(keyring => keyring.getAccounts())
.then((accounts) => {
diff --git a/app/scripts/keyrings/hd.js b/app/scripts/keyrings/hd.js
index cfec56561..80b713b58 100644
--- a/app/scripts/keyrings/hd.js
+++ b/app/scripts/keyrings/hd.js
@@ -33,15 +33,15 @@ class HdKeyring extends EventEmitter {
this.mnemonic = null
this.root = null
- if ('mnemonic' in opts) {
+ if (opts.mnemonic) {
this._initFromMnemonic(opts.mnemonic)
}
- if ('numberOfAccounts' in opts) {
- this.addAccounts(opts.numberOfAccounts)
+ if (opts.numberOfAccounts) {
+ return this.addAccounts(opts.numberOfAccounts)
}
- return Promise.resolve()
+ return Promise.resolve([])
}
addAccounts (numberOfAccounts = 1) {
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index a32842cc7..c723655fa 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -380,3 +380,14 @@ ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) {
data.gasMultiplier = gasMultiplier
this.setData(data)
}
+
+ConfigManager.prototype.setLostAccounts = function (lostAccounts) {
+ var data = this.getData()
+ data.lostAccounts = lostAccounts
+ this.setData(data)
+}
+
+ConfigManager.prototype.getLostAccounts = function () {
+ var data = this.getData()
+ return data.lostAccounts || []
+}
diff --git a/app/scripts/lib/encryptor.js b/app/scripts/lib/encryptor.js
deleted file mode 100644
index 4770d2f54..000000000
--- a/app/scripts/lib/encryptor.js
+++ /dev/null
@@ -1,156 +0,0 @@
-module.exports = {
-
- // Simple encryption methods:
- encrypt,
- decrypt,
-
- // More advanced encryption methods:
- keyFromPassword,
- encryptWithKey,
- decryptWithKey,
-
- // Buffer <-> String methods
- convertArrayBufferViewtoString,
- convertStringToArrayBufferView,
-
- // Buffer <-> Hex string methods
- serializeBufferForStorage,
- serializeBufferFromStorage,
-
- // Buffer <-> base64 string methods
- encodeBufferToBase64,
- decodeBase64ToBuffer,
-
- generateSalt,
-}
-
-// Takes a Pojo, returns cypher text.
-function encrypt (password, dataObj) {
- const salt = this.generateSalt()
-
- return keyFromPassword(password + salt)
- .then(function (passwordDerivedKey) {
- return encryptWithKey(passwordDerivedKey, dataObj)
- })
- .then(function (payload) {
- payload.salt = salt
- return JSON.stringify(payload)
- })
-}
-
-function encryptWithKey (key, dataObj) {
- var data = JSON.stringify(dataObj)
- var dataBuffer = convertStringToArrayBufferView(data)
- var vector = global.crypto.getRandomValues(new Uint8Array(16))
- return global.crypto.subtle.encrypt({
- name: 'AES-GCM',
- iv: vector,
- }, key, dataBuffer).then(function (buf) {
- var buffer = new Uint8Array(buf)
- var vectorStr = encodeBufferToBase64(vector)
- var vaultStr = encodeBufferToBase64(buffer)
- return {
- data: vaultStr,
- iv: vectorStr,
- }
- })
-}
-
-// Takes encrypted text, returns the restored Pojo.
-function decrypt (password, text) {
- const payload = JSON.parse(text)
- const salt = payload.salt
- return keyFromPassword(password + salt)
- .then(function (key) {
- return decryptWithKey(key, payload)
- })
-}
-
-function decryptWithKey (key, payload) {
- const encryptedData = decodeBase64ToBuffer(payload.data)
- const vector = decodeBase64ToBuffer(payload.iv)
- return crypto.subtle.decrypt({name: 'AES-GCM', iv: vector}, key, encryptedData)
- .then(function (result) {
- const decryptedData = new Uint8Array(result)
- const decryptedStr = convertArrayBufferViewtoString(decryptedData)
- const decryptedObj = JSON.parse(decryptedStr)
- return decryptedObj
- })
- .catch(function (reason) {
- throw new Error('Incorrect password')
- })
-}
-
-function convertStringToArrayBufferView (str) {
- var bytes = new Uint8Array(str.length)
- for (var i = 0; i < str.length; i++) {
- bytes[i] = str.charCodeAt(i)
- }
-
- return bytes
-}
-
-function convertArrayBufferViewtoString (buffer) {
- var str = ''
- for (var i = 0; i < buffer.byteLength; i++) {
- str += String.fromCharCode(buffer[i])
- }
-
- return str
-}
-
-function keyFromPassword (password) {
- var passBuffer = convertStringToArrayBufferView(password)
- return global.crypto.subtle.digest('SHA-256', passBuffer)
- .then(function (passHash) {
- return global.crypto.subtle.importKey('raw', passHash, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt'])
- })
-}
-
-function serializeBufferFromStorage (str) {
- var stripStr = (str.slice(0, 2) === '0x') ? str.slice(2) : str
- var buf = new Uint8Array(stripStr.length / 2)
- for (var i = 0; i < stripStr.length; i += 2) {
- var seg = stripStr.substr(i, 2)
- buf[i / 2] = parseInt(seg, 16)
- }
- return buf
-}
-
-// Should return a string, ready for storage, in hex format.
-function serializeBufferForStorage (buffer) {
- var result = '0x'
- var len = buffer.length || buffer.byteLength
- for (var i = 0; i < len; i++) {
- result += unprefixedHex(buffer[i])
- }
- return result
-}
-
-function unprefixedHex (num) {
- var hex = num.toString(16)
- while (hex.length < 2) {
- hex = '0' + hex
- }
- return hex
-}
-
-function encodeBufferToBase64 (buf) {
- var b64encoded = btoa(String.fromCharCode.apply(null, buf))
- return b64encoded
-}
-
-function decodeBase64ToBuffer (base64) {
- var buf = new Uint8Array(atob(base64).split('')
- .map(function (c) {
- return c.charCodeAt(0)
- }))
- return buf
-}
-
-function generateSalt (byteCount = 32) {
- var view = new Uint8Array(byteCount)
- global.crypto.getRandomValues(view)
- var b64encoded = btoa(String.fromCharCode.apply(null, view))
- return b64encoded
-}
diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js
index 40b08efee..2e9418376 100644
--- a/app/scripts/lib/idStore-migrator.js
+++ b/app/scripts/lib/idStore-migrator.js
@@ -1,5 +1,8 @@
const IdentityStore = require('./idStore')
-
+const HdKeyring = require('../keyrings/hd')
+const sigUtil = require('./sig-util')
+const normalize = sigUtil.normalize
+const denodeify = require('denodeify')
module.exports = class IdentityStoreMigrator {
@@ -23,15 +26,13 @@ module.exports = class IdentityStoreMigrator {
return Promise.resolve(null)
}
- return new Promise((resolve, reject) => {
- this.idStore.submitPassword(password, (err) => {
- if (err) return reject(err)
- try {
- resolve(this.serializeVault())
- } catch (e) {
- reject(e)
- }
- })
+ const idStore = this.idStore
+ const submitPassword = denodeify(idStore.submitPassword.bind(idStore))
+
+ return submitPassword(password)
+ .then(() => {
+ const serialized = this.serializeVault()
+ return this.checkForLostAccounts(serialized)
})
}
@@ -45,6 +46,28 @@ module.exports = class IdentityStoreMigrator {
}
}
+ checkForLostAccounts (serialized) {
+ const hd = new HdKeyring()
+ return hd.deserialize(serialized.data)
+ .then((hexAccounts) => {
+ const newAccounts = hexAccounts.map(normalize)
+ const oldAccounts = this.idStore._getAddresses().map(normalize)
+ const lostAccounts = oldAccounts.reduce((result, account) => {
+ if (newAccounts.includes(account)) {
+ return result
+ } else {
+ result.push(account)
+ return result
+ }
+ }, [])
+
+ return {
+ serialized,
+ lostAccounts,
+ }
+ })
+ }
+
hasOldVault () {
const wallet = this.configManager.getWallet()
return wallet
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 7798a6a60..125753063 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -2,6 +2,7 @@ const extend = require('xtend')
const EthStore = require('eth-store')
const MetaMaskProvider = require('web3-provider-engine/zero.js')
const KeyringController = require('./keyring-controller')
+const NoticeController = require('./notice-controller')
const messageManager = require('./lib/message-manager')
const TxManager = require('./transaction-manager')
const HostStore = require('./lib/remote-store.js').HostStore
@@ -23,6 +24,13 @@ module.exports = class MetamaskController {
txManager: this.txManager,
getNetwork: this.getStateNetwork.bind(this),
})
+ // notices
+ this.noticeController = new NoticeController({
+ configManager: this.configManager,
+ })
+ this.noticeController.updateNoticesList()
+ // to be uncommented when retrieving notices from a remote server.
+ // this.noticeController.startPolling()
this.provider = this.initializeProvider(opts)
this.ethStore = new EthStore(this.provider)
this.keyringController.setStore(this.ethStore)
@@ -57,12 +65,15 @@ module.exports = class MetamaskController {
this.configManager.getConfig(),
this.keyringController.getState(),
this.txManager.getState()
+ this.noticeController.getState()
)
}
getApi () {
const keyringController = this.keyringController
const txManager = this.txManager
+ const noticeController = this.noticeController
+
return {
getState: (cb) => { cb(null, this.getState()) },
setRpcTarget: this.setRpcTarget.bind(this),
@@ -101,6 +112,9 @@ module.exports = class MetamaskController {
buyEth: this.buyEth.bind(this),
// shapeshift
createShapeShiftTx: this.createShapeShiftTx.bind(this),
+ // notices
+ checkNotices: noticeController.updateNoticesList.bind(noticeController),
+ markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
}
}
@@ -292,7 +306,7 @@ module.exports = class MetamaskController {
setTOSHash (hash) {
try {
this.configManager.setTOSHash(hash)
- } catch (e) {
+ } catch (err) {
console.error('Error in setting terms of service hash.')
}
}
@@ -304,17 +318,19 @@ module.exports = class MetamaskController {
this.resetDisclaimer()
this.setTOSHash(global.TOS_HASH)
}
- } catch (e) {
+ } catch (err) {
console.error('Error in checking TOS change.')
}
}
+ // disclaimer
+
agreeToDisclaimer (cb) {
try {
this.configManager.setConfirmedDisclaimer(true)
cb()
- } catch (e) {
- cb(e)
+ } catch (err) {
+ cb(err)
}
}
@@ -337,8 +353,8 @@ module.exports = class MetamaskController {
conversionDate: this.configManager.getConversionDate(),
}
cb(data)
- } catch (e) {
- cb(null, e)
+ } catch (err) {
+ cb(null, err)
}
}
@@ -411,8 +427,8 @@ module.exports = class MetamaskController {
try {
this.configManager.setGasMultiplier(gasMultiplier)
cb()
- } catch (e) {
- cb(e)
+ } catch (err) {
+ cb(err)
}
}
diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js
new file mode 100644
index 000000000..00c87c670
--- /dev/null
+++ b/app/scripts/notice-controller.js
@@ -0,0 +1,96 @@
+const EventEmitter = require('events').EventEmitter
+const hardCodedNotices = require('../../development/notices.json')
+
+module.exports = class NoticeController extends EventEmitter {
+
+ constructor (opts) {
+ super()
+ this.configManager = opts.configManager
+ this.noticePoller = null
+ }
+
+ getState () {
+ var lastUnreadNotice = this.getLatestUnreadNotice()
+
+ return {
+ lastUnreadNotice: lastUnreadNotice,
+ noActiveNotices: !lastUnreadNotice,
+ }
+ }
+
+ getNoticesList () {
+ var data = this.configManager.getData()
+ if ('noticesList' in data) {
+ return data.noticesList
+ } else {
+ return []
+ }
+ }
+
+ setNoticesList (list) {
+ var data = this.configManager.getData()
+ data.noticesList = list
+ this.configManager.setData(data)
+ return Promise.resolve(true)
+ }
+
+ markNoticeRead (notice, cb) {
+ cb = cb || function (err) { if (err) throw err }
+ try {
+ var notices = this.getNoticesList()
+ var id = notice.id
+ notices[id].read = true
+ this.setNoticesList(notices)
+ const latestNotice = this.getLatestUnreadNotice()
+ cb(null, latestNotice)
+ } catch (err) {
+ cb(err)
+ }
+ }
+
+ updateNoticesList () {
+ return this._retrieveNoticeData().then((newNotices) => {
+ var oldNotices = this.getNoticesList()
+ var combinedNotices = this._mergeNotices(oldNotices, newNotices)
+ return Promise.resolve(this.setNoticesList(combinedNotices))
+ })
+ }
+
+ getLatestUnreadNotice () {
+ var notices = this.getNoticesList()
+ var filteredNotices = notices.filter((notice) => {
+ return notice.read === false
+ })
+ return filteredNotices[filteredNotices.length - 1]
+ }
+
+ startPolling () {
+ if (this.noticePoller) {
+ clearInterval(this.noticePoller)
+ }
+ this.noticePoller = setInterval(() => {
+ this.noticeController.updateNoticesList()
+ }, 300000)
+ }
+
+ _mergeNotices (oldNotices, newNotices) {
+ var noticeMap = this._mapNoticeIds(oldNotices)
+ newNotices.forEach((notice) => {
+ if (noticeMap.indexOf(notice.id) === -1) {
+ oldNotices.push(notice)
+ }
+ })
+ return oldNotices
+ }
+
+ _mapNoticeIds (notices) {
+ return notices.map((notice) => notice.id)
+ }
+
+ _retrieveNoticeData () {
+ // Placeholder for the API.
+ return Promise.resolve(hardCodedNotices)
+ }
+
+
+}