diff options
Diffstat (limited to 'app/scripts')
-rw-r--r-- | app/scripts/background.js | 129 | ||||
-rw-r--r-- | app/scripts/config.js | 39 | ||||
-rw-r--r-- | app/scripts/contentscript.js | 42 | ||||
-rw-r--r-- | app/scripts/controllers/computed-balances.js | 45 | ||||
-rw-r--r-- | app/scripts/controllers/token-rates.js | 6 | ||||
-rw-r--r-- | app/scripts/edge-encryptor.js | 142 | ||||
-rw-r--r-- | app/scripts/first-time-state.js | 16 | ||||
-rw-r--r-- | app/scripts/inpage.js | 13 | ||||
-rw-r--r-- | app/scripts/lib/createProviderMiddleware.js | 6 | ||||
-rw-r--r-- | app/scripts/lib/port-stream.js | 37 | ||||
-rw-r--r-- | app/scripts/lib/setupMetamaskMeshMetrics.js | 3 | ||||
-rw-r--r-- | app/scripts/popup-core.js | 25 |
12 files changed, 422 insertions, 81 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js index 6550e8944..38b871bb5 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,3 +1,7 @@ +/** + * @file The entry point for the web extension singleton process. + */ + const urlUtil = require('url') const endOfStream = require('end-of-stream') const pump = require('pump') @@ -61,6 +65,90 @@ initialize().catch(log.error) // setup metamask mesh testing container setupMetamaskMeshMetrics() +/** + * An object representing a transaction, in whatever state it is in. + * @typedef TransactionMeta + * + * @property {number} id - An internally unique tx identifier. + * @property {number} time - Time the tx was first suggested, in unix epoch time (ms). + * @property {string} status - The current transaction status (unapproved, signed, submitted, dropped, failed, rejected), as defined in `tx-state-manager.js`. + * @property {string} metamaskNetworkId - The transaction's network ID, used for EIP-155 compliance. + * @property {boolean} loadingDefaults - TODO: Document + * @property {Object} txParams - The tx params as passed to the network provider. + * @property {Object[]} history - A history of mutations to this TransactionMeta object. + * @property {boolean} gasPriceSpecified - True if the suggesting dapp specified a gas price, prevents auto-estimation. + * @property {boolean} gasLimitSpecified - True if the suggesting dapp specified a gas limit, prevents auto-estimation. + * @property {string} estimatedGas - A hex string represented the estimated gas limit required to complete the transaction. + * @property {string} origin - A string representing the interface that suggested the transaction. + * @property {Object} nonceDetails - A metadata object containing information used to derive the suggested nonce, useful for debugging nonce issues. + * @property {string} rawTx - A hex string of the final signed transaction, ready to submit to the network. + * @property {string} hash - A hex string of the transaction hash, used to identify the transaction on the network. + * @property {number} submittedTime - The time the transaction was submitted to the network, in Unix epoch time (ms). + */ + +/** + * The data emitted from the MetaMaskController.store EventEmitter, also used to initialize the MetaMaskController. Available in UI on React state as state.metamask. + * @typedef MetaMaskState + * @property {boolean} isInitialized - Whether the first vault has been created. + * @property {boolean} isUnlocked - Whether the vault is currently decrypted and accounts are available for selection. + * @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed. + * @property {boolean} isMascara - True if the current context is the extensionless MetaMascara project. + * @property {boolean} isPopup - Returns true if the current view is an externally-triggered notification. + * @property {string} rpcTarget - DEPRECATED - The URL of the current RPC provider. + * @property {Object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys. + * @property {Object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions. + * @property {boolean} noActiveNotices - False if there are notices the user should confirm before using the application. + * @property {Array} frequentRpcList - A list of frequently used RPCs, including custom user-provided ones. + * @property {Array} addressBook - A list of previously sent to addresses. + * @property {address} selectedTokenAddress - Used to indicate if a token is globally selected. Should be deprecated in favor of UI-centric token selection. + * @property {Object} tokenExchangeRates - Info about current token prices. + * @property {Array} tokens - Tokens held by the current user, including their balances. + * @property {Object} send - TODO: Document + * @property {Object} coinOptions - TODO: Document + * @property {boolean} useBlockie - Indicates preferred user identicon format. True for blockie, false for Jazzicon. + * @property {Object} featureFlags - An object for optional feature flags. + * @property {string} networkEndpointType - TODO: Document + * @property {boolean} isRevealingSeedWords - True if seed words are currently being recovered, and should be shown to user. + * @property {boolean} welcomeScreen - True if welcome screen should be shown. + * @property {string} currentLocale - A locale string matching the user's preferred display language. + * @property {Object} provider - The current selected network provider. + * @property {string} provider.rpcTarget - The address for the RPC API, if using an RPC API. + * @property {string} provider.type - An identifier for the type of network selected, allows MetaMask to use custom provider strategies for known networks. + * @property {string} network - A stringified number of the current network ID. + * @property {Object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values. + * @property {hex} currentBlockGasLimit - The most recently seen block gas limit, in a lower case hex prefixed string. + * @property {TransactionMeta[]} selectedAddressTxList - An array of transactions associated with the currently selected account. + * @property {Object} unapprovedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {number} unapprovedMsgCount - The number of messages in unapprovedMsgs. + * @property {Object} unapprovedPersonalMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {number} unapprovedPersonalMsgCount - The number of messages in unapprovedPersonalMsgs. + * @property {Object} unapprovedTypedMsgs - An object of messages associated with the currently selected account, mapping a unique ID to the options. + * @property {number} unapprovedTypedMsgCount - The number of messages in unapprovedTypedMsgs. + * @property {string[]} keyringTypes - An array of unique keyring identifying strings, representing available strategies for creating accounts. + * @property {Keyring[]} keyrings - An array of keyring descriptions, summarizing the accounts that are available for use, and what keyrings they belong to. + * @property {Object} computedBalances - Maps accounts to their balances, accounting for balance changes from pending transactions. + * @property {string} currentAccountTab - A view identifying string for displaying the current displayed view, allows user to have a preferred tab in the old UI (between tokens and history). + * @property {string} selectedAddress - A lower case hex string of the currently selected address. + * @property {string} currentCurrency - A string identifying the user's preferred display currency, for use in showing conversion rates. + * @property {number} conversionRate - A number representing the current exchange rate from the user's preferred currency to Ether. + * @property {number} conversionDate - A unix epoch date (ms) for the time the current conversion rate was last retrieved. + * @property {Object} infuraNetworkStatus - An object of infura network status checks. + * @property {Block[]} recentBlocks - An array of recent blocks, used to calculate an effective but cheap gas price. + * @property {Array} shapeShiftTxList - An array of objects describing shapeshift exchange attempts. + * @property {Array} lostAccounts - TODO: Remove this feature. A leftover from the version-3 migration where our seed-phrase library changed to fix a bug where some accounts were mis-generated, but we recovered the old accounts as "lost" instead of losing them. + * @property {boolean} forgottenPassword - Returns true if the user has initiated the password recovery screen, is recovering from seed phrase. + */ + +/** + * @typedef VersionedData + * @property {MetaMaskState} data - The data emitted from MetaMask controller, or used to initialize it. + * @property {Number} version - The latest migration version that has been run. + */ + +/** + * Initializes the MetaMask controller, and sets up all platform configuration. + * @returns {Promise} Setup complete. + */ async function initialize () { const initState = await loadStateFromPersistence() const initLangCode = await getFirstPreferredLangCode() @@ -72,6 +160,11 @@ async function initialize () { // State and Persistence // +/** + * Loads any stored data, prioritizing the latest storage strategy. + * Migrates that data schema in case it was last loaded on an older version. + * @returns {Promise<MetaMaskState>} Last data emitted from previous instance of MetaMask. + */ async function loadStateFromPersistence () { // migrations const migrator = new Migrator({ migrations }) @@ -134,6 +227,16 @@ async function loadStateFromPersistence () { return versionedData.data } +/** + * Initializes the MetaMask Controller with any initial state and default language. + * Configures platform-specific error reporting strategy. + * Streams emitted state updates to platform-specific storage strategy. + * Creates platform listeners for new Dapps/Contexts, and sets up their data connections to the controller. + * + * @param {Object} initState - The initial state to start the controller with, matches the state that is emitted from the controller. + * @param {String} initLangCode - The region code for the language preferred by the current user. + * @returns {Promise} After setup is complete. + */ function setupController (initState, initLangCode) { // // MetaMask Controller @@ -172,6 +275,11 @@ function setupController (initState, initLangCode) { } ) + /** + * Assigns the given state to the versioned object (with metadata), and returns that. + * @param {Object} state - The state object as emitted by the MetaMaskController. + * @returns {VersionedData} The state object wrapped in an object that includes a metadata key. + */ function versionifyData (state) { versionedData.data = state return versionedData @@ -208,6 +316,18 @@ function setupController (initState, initLangCode) { return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen } + /** + * A runtime.Port object, as provided by the browser: + * @link https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port + * @typedef Port + * @type Object + */ + + /** + * Connects a Port to the MetaMask controller via a multiplexed duplex stream. + * This method identifies trusted (MetaMask) interfaces, and connects them differently from untrusted (web pages). + * @param {Port} remotePort - The port provided by a new context. + */ function connectRemote (remotePort) { const processName = remotePort.name const isMetaMaskInternalProcess = metamaskInternalProcessHash[processName] @@ -261,7 +381,10 @@ function setupController (initState, initLangCode) { controller.messageManager.on('updateBadge', updateBadge) controller.personalMessageManager.on('updateBadge', updateBadge) - // plugin badge text + /** + * Updates the Web Extension's "badge" number, on the little fox in the toolbar. + * The number reflects the current number of pending transactions or message signatures needing user approval. + */ function updateBadge () { var label = '' var unapprovedTxCount = controller.txController.getUnapprovedTxCount() @@ -283,7 +406,9 @@ function setupController (initState, initLangCode) { // Etc... // -// popup trigger +/** + * Opens the browser popup for user confirmation + */ function triggerUi () { extension.tabs.query({ active: true }, tabs => { const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id])) 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 |