aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts
diff options
context:
space:
mode:
authorAlexander Tseung <alextsg@gmail.com>2018-04-23 00:28:29 +0800
committerAlexander Tseung <alextsg@gmail.com>2018-04-23 00:28:29 +0800
commit1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a (patch)
tree3d1ca6402fd2d11b4fe946050d9b19f0655f81be /app/scripts
parent3082d2e4ef02306d5c09fbd1032a22d2efa33324 (diff)
parent74c419f9b65841c2966d1fe3b7414bf11e09e0d8 (diff)
downloadtangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.tar
tangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.tar.gz
tangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.tar.bz2
tangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.tar.lz
tangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.tar.xz
tangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.tar.zst
tangerine-wallet-browser-1d2d9798cbd22f0d4d9ce870f55dc97d4ee4bd2a.zip
Merge branch 'master' of https://github.com/MetaMask/metamask-extension into i-4033-seed-words
Diffstat (limited to 'app/scripts')
-rw-r--r--app/scripts/config.js39
-rw-r--r--app/scripts/contentscript.js42
-rw-r--r--app/scripts/controllers/computed-balances.js45
-rw-r--r--app/scripts/controllers/token-rates.js6
-rw-r--r--app/scripts/edge-encryptor.js142
-rw-r--r--app/scripts/first-time-state.js16
-rw-r--r--app/scripts/inpage.js13
-rw-r--r--app/scripts/lib/createProviderMiddleware.js6
-rw-r--r--app/scripts/lib/port-stream.js37
-rw-r--r--app/scripts/lib/setupMetamaskMeshMetrics.js3
-rw-r--r--app/scripts/popup-core.js25
11 files changed, 295 insertions, 79 deletions
diff --git a/app/scripts/config.js b/app/scripts/config.js
index a8470ed82..e6f70ca2b 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -15,7 +15,41 @@ const BETA_UI_NETWORK_TYPE = 'networkBeta'
global.METAMASK_DEBUG = process.env.METAMASK_DEBUG
-module.exports = {
+/**
+ * @typedef {Object} UrlConfig
+ * @property {string} localhost URL of local RPC provider
+ * @property {string} mainnet URL of mainnet RPC provider
+ * @property {string} ropsten URL of Ropsten testnet RPC provider
+ * @property {string} kovan URL of Kovan testnet RPC provider
+ * @property {string} rinkeby URL of Rinkeby testnet RPC provider
+ */
+
+/**
+ * @typedef {Object} NameConfig
+ * @property {string} 3 URL of local RPC provider
+ * @property {string} 4 URL of mainnet RPC provider
+ * @property {string} 42 URL of Ropsten testnet RPC provider
+ */
+
+/**
+ * @typedef {Object} EnumConfig
+ * @property {string} DEFAULT_RPC Default network provider URL
+ * @property {string} OLD_UI_NETWORK_TYPE Network associated with old UI
+ * @property {string} BETA_UI_NETWORK_TYPE Network associated with new UI
+ */
+
+/**
+ * @typedef {Object} Config
+ * @property {UrlConfig} network Network configuration parameters
+ * @property {UrlConfig} networkBeta Beta UI network configuration parameters
+ * @property {NameConfig} networkNames Network name configuration parameters
+ * @property {EnumConfig} enums Application-wide string constants
+ */
+
+/**
+ * @type {Config}
+ **/
+const config = {
network: {
localhost: LOCALHOST_RPC_URL,
mainnet: MAINET_RPC_URL,
@@ -23,7 +57,6 @@ module.exports = {
kovan: KOVAN_RPC_URL,
rinkeby: RINKEBY_RPC_URL,
},
- // Used for beta UI
networkBeta: {
localhost: LOCALHOST_RPC_URL,
mainnet: MAINET_RPC_URL_BETA,
@@ -42,3 +75,5 @@ module.exports = {
BETA_UI_NETWORK_TYPE,
},
}
+
+module.exports = config
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index fe1766273..dbf1c6d4c 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -23,6 +23,9 @@ if (shouldInjectWeb3()) {
setupStreams()
}
+/**
+ * Creates a script tag that injects inpage.js
+ */
function setupInjection () {
try {
// inject in-page script
@@ -37,6 +40,10 @@ function setupInjection () {
}
}
+/**
+ * Sets up two-way communication streams between the
+ * browser extension and local per-page browser context
+ */
function setupStreams () {
// setup communication to page and plugin
const pageStream = new LocalMessageDuplexStream({
@@ -89,17 +96,34 @@ function setupStreams () {
mux.ignoreStream('publicConfig')
}
+
+/**
+ * Error handler for page to plugin stream disconnections
+ *
+ * @param {string} remoteLabel Remote stream name
+ * @param {Error} err Stream connection error
+ */
function logStreamDisconnectWarning (remoteLabel, err) {
let warningMsg = `MetamaskContentscript - lost connection to ${remoteLabel}`
if (err) warningMsg += '\n' + err.stack
console.warn(warningMsg)
}
+/**
+ * Determines if Web3 should be injected
+ *
+ * @returns {boolean} {@code true} if Web3 should be injected
+ */
function shouldInjectWeb3 () {
return doctypeCheck() && suffixCheck()
&& documentElementCheck() && !blacklistedDomainCheck()
}
+/**
+ * Checks the doctype of the current document if it exists
+ *
+ * @returns {boolean} {@code true} if the doctype is html or if none exists
+ */
function doctypeCheck () {
const doctype = window.document.doctype
if (doctype) {
@@ -109,6 +133,11 @@ function doctypeCheck () {
}
}
+/**
+ * Checks the current document extension
+ *
+ * @returns {boolean} {@code true} if the current extension is not prohibited
+ */
function suffixCheck () {
var prohibitedTypes = ['xml', 'pdf']
var currentUrl = window.location.href
@@ -122,6 +151,11 @@ function suffixCheck () {
return true
}
+/**
+ * Checks the documentElement of the current document
+ *
+ * @returns {boolean} {@code true} if the documentElement is an html node or if none exists
+ */
function documentElementCheck () {
var documentElement = document.documentElement.nodeName
if (documentElement) {
@@ -130,6 +164,11 @@ function documentElementCheck () {
return true
}
+/**
+ * Checks if the current domain is blacklisted
+ *
+ * @returns {boolean} {@code true} if the current domain is blacklisted
+ */
function blacklistedDomainCheck () {
var blacklistedDomains = [
'uscourts.gov',
@@ -148,6 +187,9 @@ function blacklistedDomainCheck () {
return false
}
+/**
+ * Redirects the current page to a phishing information page
+ */
function redirectToPhishingWarning () {
console.log('MetaMask - redirecting to phishing warning')
window.location.href = 'https://metamask.io/phishing.html'
diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js
index 907b087cf..1a6802f9a 100644
--- a/app/scripts/controllers/computed-balances.js
+++ b/app/scripts/controllers/computed-balances.js
@@ -2,8 +2,24 @@ const ObservableStore = require('obs-store')
const extend = require('xtend')
const BalanceController = require('./balance')
-class ComputedbalancesController {
+/**
+ * @typedef {Object} ComputedBalancesOptions
+ * @property {Object} accountTracker Account tracker store reference
+ * @property {Object} txController Token controller reference
+ * @property {Object} blockTracker Block tracker reference
+ * @property {Object} initState Initial state to populate this internal store with
+ */
+/**
+ * Background controller responsible for syncing
+ * and computing ETH balances for all accounts
+ */
+class ComputedbalancesController {
+ /**
+ * Creates a new controller instance
+ *
+ * @param {ComputedBalancesOptions} [opts] Controller configuration parameters
+ */
constructor (opts = {}) {
const { accountTracker, txController, blockTracker } = opts
this.accountTracker = accountTracker
@@ -19,6 +35,9 @@ class ComputedbalancesController {
this._initBalanceUpdating()
}
+ /**
+ * Updates balances associated with each internal address
+ */
updateAllBalances () {
Object.keys(this.balances).forEach((balance) => {
const address = balance.address
@@ -26,12 +45,23 @@ class ComputedbalancesController {
})
}
+ /**
+ * Initializes internal address tracking
+ *
+ * @private
+ */
_initBalanceUpdating () {
const store = this.accountTracker.store.getState()
this.syncAllAccountsFromStore(store)
this.accountTracker.store.subscribe(this.syncAllAccountsFromStore.bind(this))
}
+ /**
+ * Uses current account state to sync and track all
+ * addresses associated with the current account
+ *
+ * @param {{ accounts: Object }} store Account tracking state
+ */
syncAllAccountsFromStore (store) {
const upstream = Object.keys(store.accounts)
const balances = Object.keys(this.balances)
@@ -50,6 +80,13 @@ class ComputedbalancesController {
})
}
+ /**
+ * Conditionally establishes a new subscription
+ * to track an address associated with the current
+ * account
+ *
+ * @param {string} address Address to conditionally subscribe to
+ */
trackAddressIfNotAlready (address) {
const state = this.store.getState()
if (!(address in state.computedBalances)) {
@@ -57,6 +94,12 @@ class ComputedbalancesController {
}
}
+ /**
+ * Establishes a new subscription to track an
+ * address associated with the current account
+ *
+ * @param {string} address Address to conditionally subscribe to
+ */
trackAddress (address) {
const updater = new BalanceController({
address,
diff --git a/app/scripts/controllers/token-rates.js b/app/scripts/controllers/token-rates.js
index 22e3e8154..28409ea10 100644
--- a/app/scripts/controllers/token-rates.js
+++ b/app/scripts/controllers/token-rates.js
@@ -46,7 +46,7 @@ class TokenRatesController {
}
/**
- * @type {Number} - Interval used to poll for exchange rates
+ * @type {Number}
*/
set interval (interval) {
this._handle && clearInterval(this._handle)
@@ -55,7 +55,7 @@ class TokenRatesController {
}
/**
- * @type {Object} - Preferences controller instance
+ * @type {Object}
*/
set preferences (preferences) {
this._preferences && this._preferences.unsubscribe()
@@ -66,7 +66,7 @@ class TokenRatesController {
}
/**
- * @type {Array} - Array of token objects with contract addresses
+ * @type {Array}
*/
set tokens (tokens) {
this._tokens = tokens
diff --git a/app/scripts/edge-encryptor.js b/app/scripts/edge-encryptor.js
index 24c0c93a8..dcb06873b 100644
--- a/app/scripts/edge-encryptor.js
+++ b/app/scripts/edge-encryptor.js
@@ -1,69 +1,97 @@
const asmcrypto = require('asmcrypto.js')
const Unibabel = require('browserify-unibabel')
+/**
+ * A Microsoft Edge-specific encryption class that exposes
+ * the interface expected by eth-keykeyring-controller
+ */
class EdgeEncryptor {
+ /**
+ * Encrypts an arbitrary object to ciphertext
+ *
+ * @param {string} password Used to generate a key to encrypt the data
+ * @param {Object} dataObject Data to encrypt
+ * @returns {Promise<string>} Promise resolving to an object with ciphertext
+ */
+ encrypt (password, dataObject) {
+ var salt = this._generateSalt()
+ return this._keyFromPassword(password, salt)
+ .then(function (key) {
+ var data = JSON.stringify(dataObject)
+ var dataBuffer = Unibabel.utf8ToBuffer(data)
+ var vector = global.crypto.getRandomValues(new Uint8Array(16))
+ var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector)
- encrypt (password, dataObject) {
+ var buffer = new Uint8Array(resultbuffer)
+ var vectorStr = Unibabel.bufferToBase64(vector)
+ var vaultStr = Unibabel.bufferToBase64(buffer)
+ return JSON.stringify({
+ data: vaultStr,
+ iv: vectorStr,
+ salt: salt,
+ })
+ })
+ }
- var salt = this._generateSalt()
- return this._keyFromPassword(password, salt)
- .then(function (key) {
+ /**
+ * Decrypts an arbitrary object from ciphertext
+ *
+ * @param {string} password Used to generate a key to decrypt the data
+ * @param {string} text Ciphertext of an encrypted object
+ * @returns {Promise<Object>} Promise resolving to copy of decrypted object
+ */
+ decrypt (password, text) {
+ const payload = JSON.parse(text)
+ const salt = payload.salt
+ return this._keyFromPassword(password, salt)
+ .then(function (key) {
+ const encryptedData = Unibabel.base64ToBuffer(payload.data)
+ const vector = Unibabel.base64ToBuffer(payload.iv)
+ return new Promise((resolve, reject) => {
+ var result
+ try {
+ result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector)
+ } catch (err) {
+ return reject(new Error('Incorrect password'))
+ }
+ const decryptedData = new Uint8Array(result)
+ const decryptedStr = Unibabel.bufferToUtf8(decryptedData)
+ const decryptedObj = JSON.parse(decryptedStr)
+ resolve(decryptedObj)
+ })
+ })
+ }
- var data = JSON.stringify(dataObject)
- var dataBuffer = Unibabel.utf8ToBuffer(data)
- var vector = global.crypto.getRandomValues(new Uint8Array(16))
- var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector)
+ /**
+ * Retrieves a cryptographic key using a password
+ *
+ * @private
+ * @param {string} password Password used to unlock a cryptographic key
+ * @param {string} salt Random base64 data
+ * @returns {Promise<Object>} Promise resolving to a derived key
+ */
+ _keyFromPassword (password, salt) {
- var buffer = new Uint8Array(resultbuffer)
- var vectorStr = Unibabel.bufferToBase64(vector)
- var vaultStr = Unibabel.bufferToBase64(buffer)
- return JSON.stringify({
- data: vaultStr,
- iv: vectorStr,
- salt: salt,
- })
- })
- }
+ var passBuffer = Unibabel.utf8ToBuffer(password)
+ var saltBuffer = Unibabel.base64ToBuffer(salt)
+ return new Promise((resolve) => {
+ var key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000)
+ resolve(key)
+ })
+ }
- decrypt (password, text) {
-
- const payload = JSON.parse(text)
- const salt = payload.salt
- return this._keyFromPassword(password, salt)
- .then(function (key) {
- const encryptedData = Unibabel.base64ToBuffer(payload.data)
- const vector = Unibabel.base64ToBuffer(payload.iv)
- return new Promise((resolve, reject) => {
- var result
- try {
- result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector)
- } catch (err) {
- return reject(new Error('Incorrect password'))
- }
- const decryptedData = new Uint8Array(result)
- const decryptedStr = Unibabel.bufferToUtf8(decryptedData)
- const decryptedObj = JSON.parse(decryptedStr)
- resolve(decryptedObj)
- })
- })
- }
-
- _keyFromPassword (password, salt) {
-
- var passBuffer = Unibabel.utf8ToBuffer(password)
- var saltBuffer = Unibabel.base64ToBuffer(salt)
- return new Promise((resolve) => {
- var key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000)
- resolve(key)
- })
- }
-
- _generateSalt (byteCount = 32) {
- var view = new Uint8Array(byteCount)
- global.crypto.getRandomValues(view)
- var b64encoded = btoa(String.fromCharCode.apply(null, view))
- return b64encoded
- }
+ /**
+ * Generates random base64 encoded data
+ *
+ * @private
+ * @returns {string} Randomized base64 encoded data
+ */
+ _generateSalt (byteCount = 32) {
+ var view = new Uint8Array(byteCount)
+ global.crypto.getRandomValues(view)
+ var b64encoded = btoa(String.fromCharCode.apply(null, view))
+ return b64encoded
+ }
}
module.exports = EdgeEncryptor
diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js
index 3063df627..144534f43 100644
--- a/app/scripts/first-time-state.js
+++ b/app/scripts/first-time-state.js
@@ -2,10 +2,16 @@
const env = process.env.METAMASK_ENV
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
-//
-// The default state of MetaMask
-//
-module.exports = {
+/**
+ * @typedef {Object} FirstTimeState
+ * @property {Object} config Initial configuration parameters
+ * @property {Object} NetworkController Network controller state
+ */
+
+/**
+ * @type {FirstTimeState}
+ */
+const initialState = {
config: {},
NetworkController: {
provider: {
@@ -13,3 +19,5 @@ module.exports = {
},
},
}
+
+module.exports = initialState
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 92c732813..6d16eebd4 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -42,20 +42,20 @@ log.debug('MetaMask - injected web3')
setupDappAutoReload(web3, inpageProvider.publicConfigStore)
// set web3 defaultAccount
-
inpageProvider.publicConfigStore.subscribe(function (state) {
web3.eth.defaultAccount = state.selectedAddress
})
-//
-// util
-//
-
// need to make sure we aren't affected by overlapping namespaces
// and that we dont affect the app with our namespace
// mostly a fix for web3's BigNumber if AMD's "define" is defined...
var __define
+/**
+ * Caches reference to global define object and deletes it to
+ * avoid conflicts with other global define objects, such as
+ * AMD's define function
+ */
function cleanContextForImports () {
__define = global.define
try {
@@ -65,6 +65,9 @@ function cleanContextForImports () {
}
}
+/**
+ * Restores global define object from cached reference
+ */
function restoreContextAfterImports () {
try {
global.define = __define
diff --git a/app/scripts/lib/createProviderMiddleware.js b/app/scripts/lib/createProviderMiddleware.js
index 4e667bac2..8a939ba4e 100644
--- a/app/scripts/lib/createProviderMiddleware.js
+++ b/app/scripts/lib/createProviderMiddleware.js
@@ -1,6 +1,10 @@
module.exports = createProviderMiddleware
-// forward requests to provider
+/**
+ * Forwards an HTTP request to the current Web3 provider
+ *
+ * @param {{ provider: Object }} config Configuration containing current Web3 provider
+ */
function createProviderMiddleware ({ provider }) {
return (req, res, next, end) => {
provider.sendAsync(req, (err, _res) => {
diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js
index a9716fb00..5c4224fd9 100644
--- a/app/scripts/lib/port-stream.js
+++ b/app/scripts/lib/port-stream.js
@@ -6,6 +6,13 @@ module.exports = PortDuplexStream
inherits(PortDuplexStream, Duplex)
+/**
+ * Creates a stream that's both readable and writable.
+ * The stream supports arbitrary objects.
+ *
+ * @class
+ * @param {Object} port Remote Port object
+ */
function PortDuplexStream (port) {
Duplex.call(this, {
objectMode: true,
@@ -15,8 +22,13 @@ function PortDuplexStream (port) {
port.onDisconnect.addListener(this._onDisconnect.bind(this))
}
-// private
-
+/**
+ * Callback triggered when a message is received from
+ * the remote Port associated with this Stream.
+ *
+ * @private
+ * @param {Object} msg - Payload from the onMessage listener of Port
+ */
PortDuplexStream.prototype._onMessage = function (msg) {
if (Buffer.isBuffer(msg)) {
delete msg._isBuffer
@@ -27,14 +39,31 @@ PortDuplexStream.prototype._onMessage = function (msg) {
}
}
+/**
+ * Callback triggered when the remote Port
+ * associated with this Stream disconnects.
+ *
+ * @private
+ */
PortDuplexStream.prototype._onDisconnect = function () {
this.destroy()
}
-// stream plumbing
-
+/**
+ * Explicitly sets read operations to a no-op
+ */
PortDuplexStream.prototype._read = noop
+
+/**
+ * Called internally when data should be written to
+ * this writable stream.
+ *
+ * @private
+ * @param {*} msg Arbitrary object to write
+ * @param {string} encoding Encoding to use when writing payload
+ * @param {Function} cb Called when writing is complete or an error occurs
+ */
PortDuplexStream.prototype._write = function (msg, encoding, cb) {
try {
if (Buffer.isBuffer(msg)) {
diff --git a/app/scripts/lib/setupMetamaskMeshMetrics.js b/app/scripts/lib/setupMetamaskMeshMetrics.js
index 40343f017..02690a948 100644
--- a/app/scripts/lib/setupMetamaskMeshMetrics.js
+++ b/app/scripts/lib/setupMetamaskMeshMetrics.js
@@ -1,6 +1,9 @@
module.exports = setupMetamaskMeshMetrics
+/**
+ * Injects an iframe into the current document for testing
+ */
function setupMetamaskMeshMetrics() {
const testingContainer = document.createElement('iframe')
testingContainer.src = 'https://metamask.github.io/mesh-testing/'
diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js
index 2e4334bb1..6325b8a8d 100644
--- a/app/scripts/popup-core.js
+++ b/app/scripts/popup-core.js
@@ -7,10 +7,14 @@ const launchMetamaskUi = require('../../ui')
const StreamProvider = require('web3-stream-provider')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
-
module.exports = initializePopup
-
+/**
+ * Asynchronously initializes the MetaMask popup UI
+ *
+ * @param {{ container: Element, connectionStream: * }} config Popup configuration object
+ * @param {Function} cb Called when initialization is complete
+ */
function initializePopup ({ container, connectionStream }, cb) {
// setup app
async.waterfall([
@@ -19,6 +23,12 @@ function initializePopup ({ container, connectionStream }, cb) {
], cb)
}
+/**
+ * Establishes streamed connections to background scripts and a Web3 provider
+ *
+ * @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
+ * @param {Function} cb Called when controller connection is established
+ */
function connectToAccountManager (connectionStream, cb) {
// setup communication with background
// setup multiplexing
@@ -28,6 +38,11 @@ function connectToAccountManager (connectionStream, cb) {
setupWeb3Connection(mx.createStream('provider'))
}
+/**
+ * Establishes a streamed connection to a Web3 provider
+ *
+ * @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
+ */
function setupWeb3Connection (connectionStream) {
var providerStream = new StreamProvider()
providerStream.pipe(connectionStream).pipe(providerStream)
@@ -38,6 +53,12 @@ function setupWeb3Connection (connectionStream) {
global.eth = new Eth(providerStream)
}
+/**
+ * Establishes a streamed connection to the background account manager
+ *
+ * @param {PortDuplexStream} connectionStream PortStream instance establishing a background connection
+ * @param {Function} cb Called when the remote account manager connection is established
+ */
function setupControllerConnection (connectionStream, cb) {
// this is a really sneaky way of adding EventEmitter api
// to a bi-directional dnode instance