aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
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.js6
-rw-r--r--app/scripts/lib/encryptor.js156
-rw-r--r--app/scripts/lib/idStore.js4
-rw-r--r--app/scripts/metamask-controller.js33
-rw-r--r--app/scripts/notice-controller.js96
8 files changed, 137 insertions, 181 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 6a087c918..2888e58a9 100644
--- a/app/scripts/keyring-controller.js
+++ b/app/scripts/keyring-controller.js
@@ -5,8 +5,9 @@ const bip39 = require('bip39')
const Transaction = require('ethereumjs-tx')
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
@@ -73,7 +74,7 @@ module.exports = class KeyringController extends EventEmitter {
// or accept a state-resolving promise to consume their results.
//
// Not all methods end with this, that might be a nice refactor.
- fullUpdate() {
+ fullUpdate () {
this.emit('update')
return Promise.resolve(this.getState())
}
@@ -587,7 +588,6 @@ module.exports = class KeyringController extends EventEmitter {
// Attempts to sign the provided @object msgParams.
signMessage (msgParams, cb) {
try {
-
const msgId = msgParams.metamaskId
delete msgParams.metamaskId
const approvalCb = this._unconfMsgCbs[msgId] || noop
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.js b/app/scripts/lib/idStore.js
index d36504f13..cf4353e48 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -258,7 +258,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
function estimateGas (cb) {
var estimationParams = extend(txParams)
- query.getBlockByNumber('latest', true, function(err, block){
+ query.getBlockByNumber('latest', true, function (err, block) {
if (err) return cb(err)
// check if gasLimit is already specified
const gasLimitSpecified = Boolean(txParams.gas)
@@ -267,7 +267,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
estimationParams.gas = block.gasLimit
}
// run tx, see if it will OOG
- query.estimateGas(estimationParams, function(err, estimatedGasHex){
+ query.estimateGas(estimationParams, function (err, estimatedGasHex) {
if (err) return cb(err.message || err)
// all gas used - must be an error
if (estimatedGasHex === estimationParams.gas) {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index ae761c753..4b8fa4323 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 HostStore = require('./lib/remote-store.js').HostStore
const Web3 = require('web3')
@@ -22,6 +23,13 @@ module.exports = class MetamaskController {
configManager: this.configManager,
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)
@@ -43,12 +51,14 @@ module.exports = class MetamaskController {
this.state,
this.ethStore.getState(),
this.configManager.getConfig(),
- this.keyringController.getState()
+ this.keyringController.getState(),
+ this.noticeController.getState()
)
}
getApi () {
const keyringController = this.keyringController
+ const noticeController = this.noticeController
return {
getState: (cb) => { cb(null, this.getState()) },
@@ -85,6 +95,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),
}
}
@@ -268,7 +281,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.')
}
}
@@ -280,17 +293,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)
}
}
@@ -313,8 +328,8 @@ module.exports = class MetamaskController {
conversionDate: this.configManager.getConversionDate(),
}
cb(data)
- } catch (e) {
- cb(null, e)
+ } catch (err) {
+ cb(null, err)
}
}
@@ -387,8 +402,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..438f5c27e
--- /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)
+ let 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)
+ }
+
+
+}