aboutsummaryrefslogtreecommitdiffstats
path: root/app/scripts
diff options
context:
space:
mode:
authorThomas <tmashuang@gmail.com>2018-03-22 03:25:41 +0800
committerThomas <tmashuang@gmail.com>2018-03-22 03:25:41 +0800
commitd646f377416ea6bfcd7682d21e011be5fa65cd3f (patch)
treedd5a1116920bc73738dcc1fefcc649927230a369 /app/scripts
parent6306c7b1901fa831e25ddd29607618122f805749 (diff)
parent072dd7ea2f7ce189b551b9fc8fd8e58aaaec4158 (diff)
downloadtangerine-wallet-browser-d646f377416ea6bfcd7682d21e011be5fa65cd3f.tar.gz
tangerine-wallet-browser-d646f377416ea6bfcd7682d21e011be5fa65cd3f.tar.zst
tangerine-wallet-browser-d646f377416ea6bfcd7682d21e011be5fa65cd3f.zip
Merge branch 'master' into selenium-e2e
Diffstat (limited to 'app/scripts')
-rw-r--r--app/scripts/README.md14
-rw-r--r--app/scripts/contentscript.js17
-rw-r--r--app/scripts/controllers/README.md4
-rw-r--r--app/scripts/controllers/transactions.js53
-rw-r--r--app/scripts/lib/tx-state-manager.js42
-rw-r--r--app/scripts/metamask-controller.js599
-rw-r--r--app/scripts/migrations/README.md5
7 files changed, 457 insertions, 277 deletions
diff --git a/app/scripts/README.md b/app/scripts/README.md
new file mode 100644
index 000000000..f5a907244
--- /dev/null
+++ b/app/scripts/README.md
@@ -0,0 +1,14 @@
+# Main MetaMask Code
+
+This folder contains the core-code.
+
+Currently, it is organized mostly based on file category, like:
+
+controllers, migrations, lib
+
+## Ongoing Task
+
+Refactor code-structure, thus the subsystems are reflected on the filesystem.
+
+### Examples
+
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 2ed7c87b6..7abbc60e7 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -96,7 +96,8 @@ function logStreamDisconnectWarning (remoteLabel, err) {
}
function shouldInjectWeb3 () {
- return doctypeCheck() && suffixCheck() && documentElementCheck()
+ return doctypeCheck() && suffixCheck()
+ && documentElementCheck() && !blacklistedDomainCheck()
}
function doctypeCheck () {
@@ -129,6 +130,20 @@ function documentElementCheck () {
return true
}
+function blacklistedDomainCheck () {
+ var blacklistedDomains = ['uscourts.gov', 'dropbox.com']
+ var currentUrl = window.location.href
+ var currentRegex
+ for (let i = 0; i < blacklistedDomains.length; i++) {
+ const blacklistedDomain = blacklistedDomains[i].replace('.', '\\.')
+ currentRegex = new RegExp(`(?:https?:\\/\\/)(?:(?!${blacklistedDomain}).)*$`)
+ if (!currentRegex.test(currentUrl)) {
+ return true
+ }
+ }
+ return false
+}
+
function redirectToPhishingWarning () {
console.log('MetaMask - redirecting to phishing warning')
window.location.href = 'https://metamask.io/phishing.html'
diff --git a/app/scripts/controllers/README.md b/app/scripts/controllers/README.md
new file mode 100644
index 000000000..392c0457d
--- /dev/null
+++ b/app/scripts/controllers/README.md
@@ -0,0 +1,4 @@
+# Controllers
+
+Different controllers (in the sense of *VC *View-Controller).
+
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 9c2ca0dc8..3e3909361 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -6,7 +6,6 @@ const EthQuery = require('ethjs-query')
const TransactionStateManager = require('../lib/tx-state-manager')
const TxGasUtil = require('../lib/tx-gas-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
-const createId = require('../lib/random-id')
const NonceTracker = require('../lib/nonce-tracker')
/*
@@ -92,8 +91,8 @@ module.exports = class TransactionController extends EventEmitter {
this.pendingTxTracker.on('tx:warning', (txMeta) => {
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
})
+ this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId))
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
- this.pendingTxTracker.on('tx:confirmed', this.txStateManager.setTxStatusConfirmed.bind(this.txStateManager))
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber
@@ -186,14 +185,7 @@ module.exports = class TransactionController extends EventEmitter {
// validate
await this.txGasUtil.validateTxParams(txParams)
// construct txMeta
- const txMeta = {
- id: createId(),
- time: (new Date()).getTime(),
- status: 'unapproved',
- metamaskNetworkId: this.getNetwork(),
- txParams: txParams,
- loadingDefaults: true,
- }
+ const txMeta = this.txStateManager.generateTxMeta({txParams})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
// add default tx params
@@ -215,7 +207,6 @@ module.exports = class TransactionController extends EventEmitter {
const txParams = txMeta.txParams
// ensure value
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
- txMeta.nonceSpecified = Boolean(txParams.nonce)
let gasPrice = txParams.gasPrice
if (!gasPrice) {
gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice()
@@ -226,11 +217,17 @@ module.exports = class TransactionController extends EventEmitter {
return await this.txGasUtil.analyzeGasUsage(txMeta)
}
- async retryTransaction (txId) {
- this.txStateManager.setTxStatusUnapproved(txId)
- const txMeta = this.txStateManager.getTx(txId)
- txMeta.lastGasPrice = txMeta.txParams.gasPrice
- this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry')
+ async retryTransaction (originalTxId) {
+ const originalTxMeta = this.txStateManager.getTx(originalTxId)
+ const lastGasPrice = originalTxMeta.txParams.gasPrice
+ const txMeta = this.txStateManager.generateTxMeta({
+ txParams: originalTxMeta.txParams,
+ lastGasPrice,
+ loadingDefaults: false,
+ })
+ this.addTx(txMeta)
+ this.emit('newUnapprovedTx', txMeta)
+ return txMeta
}
async updateTransaction (txMeta) {
@@ -253,11 +250,9 @@ module.exports = class TransactionController extends EventEmitter {
// wait for a nonce
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
// add nonce to txParams
- const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce
- if (nonce > nonceLock.nextNonce) {
- const message = `Specified nonce may not be larger than account's next valid nonce.`
- throw new Error(message)
- }
+ // if txMeta has lastGasPrice then it is a retry at same nonce with higher
+ // gas price transaction and their for the nonce should not be calculated
+ const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
// add nonce debugging information to txMeta
txMeta.nonceDetails = nonceLock.nonceDetails
@@ -314,6 +309,22 @@ module.exports = class TransactionController extends EventEmitter {
// PRIVATE METHODS
//
+ _markNonceDuplicatesDropped (txId) {
+ this.txStateManager.setTxStatusConfirmed(txId)
+ // get the confirmed transactions nonce and from address
+ const txMeta = this.txStateManager.getTx(txId)
+ const { nonce, from } = txMeta.txParams
+ const sameNonceTxs = this.txStateManager.getFilteredTxList({nonce, from})
+ if (!sameNonceTxs.length) return
+ // mark all same nonce transactions as dropped and give i a replacedBy hash
+ sameNonceTxs.forEach((otherTxMeta) => {
+ if (otherTxMeta.id === txId) return
+ otherTxMeta.replacedBy = txMeta.hash
+ this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce')
+ this.txStateManager.setTxStatusDropped(otherTxMeta.id)
+ })
+ }
+
_updateMemstore () {
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index 2eb006380..ad07c813f 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -1,9 +1,21 @@
const extend = require('xtend')
const EventEmitter = require('events')
const ObservableStore = require('obs-store')
+const createId = require('./random-id')
const ethUtil = require('ethereumjs-util')
const txStateHistoryHelper = require('./tx-state-history-helper')
+// STATUS METHODS
+ // statuses:
+ // - `'unapproved'` the user has not responded
+ // - `'rejected'` the user has responded no!
+ // - `'approved'` the user has approved the tx
+ // - `'signed'` the tx is signed
+ // - `'submitted'` the tx is sent to a server
+ // - `'confirmed'` the tx has been included in a block.
+ // - `'failed'` the tx failed for some reason, included on tx data.
+ // - `'dropped'` the tx nonce was already used
+
module.exports = class TransactionStateManager extends EventEmitter {
constructor ({ initState, txHistoryLimit, getNetwork }) {
super()
@@ -16,6 +28,16 @@ module.exports = class TransactionStateManager extends EventEmitter {
this.getNetwork = getNetwork
}
+ generateTxMeta (opts) {
+ return extend({
+ id: createId(),
+ time: (new Date()).getTime(),
+ status: 'unapproved',
+ metamaskNetworkId: this.getNetwork(),
+ loadingDefaults: true,
+ }, opts)
+ }
+
// Returns the number of txs for the current network.
getTxCount () {
return this.getTxList().length
@@ -164,16 +186,6 @@ module.exports = class TransactionStateManager extends EventEmitter {
})
}
- // STATUS METHODS
- // statuses:
- // - `'unapproved'` the user has not responded
- // - `'rejected'` the user has responded no!
- // - `'approved'` the user has approved the tx
- // - `'signed'` the tx is signed
- // - `'submitted'` the tx is sent to a server
- // - `'confirmed'` the tx has been included in a block.
- // - `'failed'` the tx failed for some reason, included on tx data.
-
// get::set status
// should return the status of the tx.
@@ -202,7 +214,11 @@ module.exports = class TransactionStateManager extends EventEmitter {
}
// should update the status of the tx to 'submitted'.
+ // and add a time stamp for when it was called
setTxStatusSubmitted (txId) {
+ const txMeta = this.getTx(txId)
+ txMeta.submittedTime = (new Date()).getTime()
+ this.updateTx(txMeta, 'txStateManager - add submitted time stamp')
this._setTxStatus(txId, 'submitted')
}
@@ -211,6 +227,12 @@ module.exports = class TransactionStateManager extends EventEmitter {
this._setTxStatus(txId, 'confirmed')
}
+ // should update the status dropped
+ setTxStatusDropped (txId) {
+ this._setTxStatus(txId, 'dropped')
+ }
+
+
setTxStatusFailed (txId, err) {
const txMeta = this.getTx(txId)
txMeta.err = {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 0a5c1d36f..18d71874a 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -1,3 +1,9 @@
+/**
+ * @file The central metamask controller. Aggregates other controllers and exports an api.
+ * @copyright Copyright (c) 2018 MetaMask
+ * @license MIT
+ */
+
const EventEmitter = require('events')
const extend = require('xtend')
const pump = require('pump')
@@ -41,7 +47,11 @@ const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
module.exports = class MetamaskController extends EventEmitter {
- constructor (opts) {
+ /**
+ * @constructor
+ * @param {Object} opts
+ */
+ constructor (opts) {
super()
this.defaultMaxListeners = 20
@@ -223,10 +233,9 @@ module.exports = class MetamaskController extends EventEmitter {
this.infuraController.store.subscribe(sendUpdate)
}
- //
- // Constructor helpers
- //
-
+ /**
+ * Constructor helper: initialize a provider.
+ */
initializeProvider () {
const providerOpts = {
static: {
@@ -257,6 +266,9 @@ module.exports = class MetamaskController extends EventEmitter {
return providerProxy
}
+ /**
+ * Constructor helper: initialize a public config store.
+ */
initPublicConfigStore () {
// get init state
const publicConfigStore = new ObservableStore()
@@ -278,10 +290,15 @@ module.exports = class MetamaskController extends EventEmitter {
return publicConfigStore
}
- //
- // State Management
- //
+//=============================================================================
+// EXPOSED TO THE UI SUBSYSTEM
+//=============================================================================
+ /**
+ * The metamask-state of the various controllers, made available to the UI
+ *
+ * @returns {Object} status
+ */
getState () {
const wallet = this.configManager.getWallet()
const vault = this.keyringController.store.getState().vault
@@ -316,10 +333,11 @@ module.exports = class MetamaskController extends EventEmitter {
)
}
- //
- // Remote Features
- //
-
+ /**
+ * Returns an api-object which is consumed by the UI
+ *
+ * @returns {Object}
+ */
getApi () {
const keyringController = this.keyringController
const preferencesController = this.preferencesController
@@ -400,127 +418,24 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
- setupUntrustedCommunication (connectionStream, originDomain) {
- // Check if new connection is blacklisted
- if (this.blacklistController.checkForPhishing(originDomain)) {
- log.debug('MetaMask - sending phishing warning for', originDomain)
- this.sendPhishingWarning(connectionStream, originDomain)
- return
- }
-
- // setup multiplexing
- const mux = setupMultiplex(connectionStream)
- // connect features
- this.setupProviderConnection(mux.createStream('provider'), originDomain)
- this.setupPublicConfig(mux.createStream('publicConfig'))
- }
-
- setupTrustedCommunication (connectionStream, originDomain) {
- // setup multiplexing
- const mux = setupMultiplex(connectionStream)
- // connect features
- this.setupControllerConnection(mux.createStream('controller'))
- this.setupProviderConnection(mux.createStream('provider'), originDomain)
- }
-
- sendPhishingWarning (connectionStream, hostname) {
- const mux = setupMultiplex(connectionStream)
- const phishingStream = mux.createStream('phishing')
- phishingStream.write({ hostname })
- }
-
- setupControllerConnection (outStream) {
- const api = this.getApi()
- const dnode = Dnode(api)
- pump(
- outStream,
- dnode,
- outStream,
- (err) => {
- if (err) log.error(err)
- }
- )
- dnode.on('remote', (remote) => {
- // push updates to popup
- const sendUpdate = remote.sendUpdate.bind(remote)
- this.on('update', sendUpdate)
- })
- }
-
- setupProviderConnection (outStream, origin) {
- // setup json rpc engine stack
- const engine = new RpcEngine()
-
- // create filter polyfill middleware
- const filterMiddleware = createFilterMiddleware({
- provider: this.provider,
- blockTracker: this.provider._blockTracker,
- })
-
- engine.push(createOriginMiddleware({ origin }))
- engine.push(createLoggerMiddleware({ origin }))
- engine.push(filterMiddleware)
- engine.push(createProviderMiddleware({ provider: this.provider }))
-
- // setup connection
- const providerStream = createEngineStream({ engine })
- pump(
- outStream,
- providerStream,
- outStream,
- (err) => {
- // cleanup filter polyfill middleware
- filterMiddleware.destroy()
- if (err) log.error(err)
- }
- )
- }
-
- setupPublicConfig (outStream) {
- pump(
- asStream(this.publicConfigStore),
- outStream,
- (err) => {
- if (err) log.error(err)
- }
- )
- }
- privateSendUpdate () {
- this.emit('update', this.getState())
- }
- getGasPrice () {
- const { recentBlocksController } = this
- const { recentBlocks } = recentBlocksController.store.getState()
-
- // Return 1 gwei if no blocks have been observed:
- if (recentBlocks.length === 0) {
- return '0x' + GWEI_BN.toString(16)
- }
-
- const lowestPrices = recentBlocks.map((block) => {
- if (!block.gasPrices || block.gasPrices.length < 1) {
- return GWEI_BN
- }
- return block.gasPrices
- .map(hexPrefix => hexPrefix.substr(2))
- .map(hex => new BN(hex, 16))
- .sort((a, b) => {
- return a.gt(b) ? 1 : -1
- })[0]
- })
- .map(number => number.div(GWEI_BN).toNumber())
-
- const percentileNum = percentile(50, lowestPrices)
- const percentileNumBn = new BN(percentileNum)
- return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
- }
-
- //
- // Vault Management
- //
+//=============================================================================
+// VAULT / KEYRING RELATED METHODS
+//=============================================================================
+ /**
+ * Creates a new Vault(?) and create a new keychain(?)
+ *
+ * A vault is ...
+ *
+ * A keychain is ...
+ *
+ *
+ * @param {} password
+ *
+ * @returns {} vault
+ */
async createNewVaultAndKeychain (password) {
const release = await this.createVaultMutex.acquire()
let vault
@@ -544,6 +459,11 @@ module.exports = class MetamaskController extends EventEmitter {
return vault
}
+ /**
+ * Create a new Vault and restore an existent keychain
+ * @param {} password
+ * @param {} seed
+ */
async createNewVaultAndRestore (password, seed) {
const release = await this.createVaultMutex.acquire()
try {
@@ -557,16 +477,28 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /**
+ * Retrieves the first Identiy from the passed Vault and selects the related address
+ *
+ * An Identity is ...
+ *
+ * @param {} vault
+ */
selectFirstIdentity (vault) {
const { identities } = vault
const address = Object.keys(identities)[0]
this.preferencesController.setSelectedAddress(address)
}
- //
+ // ?
// Opinionated Keyring Management
//
+ /**
+ * Adds a new account to ...
+ *
+ * @returns {} keyState
+ */
async addNewAccount () {
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
if (!primaryKeyring) {
@@ -588,10 +520,12 @@ module.exports = class MetamaskController extends EventEmitter {
return keyState
}
- // Adds the current vault's seed words to the UI's state tree.
- //
- // Used when creating a first vault, to allow confirmation.
- // Also used when revealing the seed words in the confirmation view.
+ /**
+ * Adds the current vault's seed words to the UI's state tree.
+ *
+ * Used when creating a first vault, to allow confirmation.
+ * Also used when revealing the seed words in the confirmation view.
+ */
placeSeedWords (cb) {
this.verifySeedPhrase()
@@ -604,10 +538,13 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
- // Verifies the current vault's seed words if they can restore the
- // accounts belonging to the current vault.
- //
- // Called when the first account is created and on unlocking the vault.
+ /**
+ * Verifies the validity of the current vault's seed phrase.
+ *
+ * Validity: seed phrase restores the accounts belonging to the current vault.
+ *
+ * Called when the first account is created and on unlocking the vault.
+ */
async verifySeedPhrase () {
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
@@ -632,22 +569,33 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
- // ClearSeedWordCache
- //
- // Removes the primary account's seed words from the UI's state tree,
- // ensuring they are only ever available in the background process.
+ /**
+ * Remove the primary account seed phrase from the UI's state tree.
+ *
+ * The seed phrase remains available in the background process.
+ *
+ */
clearSeedWordCache (cb) {
this.configManager.setSeedWords(null)
cb(null, this.preferencesController.getSelectedAddress())
}
-
+
+ /**
+ * ?
+ */
resetAccount (cb) {
const selectedAddress = this.preferencesController.getSelectedAddress()
this.txController.wipeTransactions(selectedAddress)
cb(null, selectedAddress)
}
-
+ /**
+ * Imports an account ... ?
+ *
+ * @param {} strategy
+ * @param {} args
+ * @param {} cb
+ */
importAccountWithStrategy (strategy, args, cb) {
accountImporter.importAccount(strategy, args)
.then((privateKey) => {
@@ -659,11 +607,150 @@ module.exports = class MetamaskController extends EventEmitter {
.catch((reason) => { cb(reason) })
}
+ // ---------------------------------------------------------------------------
+ // Identity Management (sign)
- //
- // Identity Management
- //
- //
+ /**
+ * @param {} msgParams
+ * @param {} cb
+ */
+ signMessage (msgParams, cb) {
+ log.info('MetaMaskController - signMessage')
+ const msgId = msgParams.metamaskId
+
+ // sets the status op the message to 'approved'
+ // and removes the metamaskId for signing
+ return this.messageManager.approveMessage(msgParams)
+ .then((cleanMsgParams) => {
+ // signs the message
+ return this.keyringController.signMessage(cleanMsgParams)
+ })
+ .then((rawSig) => {
+ // tells the listener that the message has been signed
+ // and can be returned to the dapp
+ this.messageManager.setMsgStatusSigned(msgId, rawSig)
+ return this.getState()
+ })
+ }
+
+ // Prefixed Style Message Signing Methods:
+
+ /**
+ *
+ * @param {} msgParams
+ * @param {} cb
+ */
+ approvePersonalMessage (msgParams, cb) {
+ const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
+ this.sendUpdate()
+ this.opts.showUnconfirmedMessage()
+ this.personalMessageManager.once(`${msgId}:finished`, (data) => {
+ switch (data.status) {
+ case 'signed':
+ return cb(null, data.rawSig)
+ case 'rejected':
+ return cb(new Error('MetaMask Message Signature: User denied transaction signature.'))
+ default:
+ return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+ }
+ })
+ }
+
+ /**
+ * @param {} msgParams
+ */
+ signPersonalMessage (msgParams) {
+ log.info('MetaMaskController - signPersonalMessage')
+ const msgId = msgParams.metamaskId
+ // sets the status op the message to 'approved'
+ // and removes the metamaskId for signing
+ return this.personalMessageManager.approveMessage(msgParams)
+ .then((cleanMsgParams) => {
+ // signs the message
+ return this.keyringController.signPersonalMessage(cleanMsgParams)
+ })
+ .then((rawSig) => {
+ // tells the listener that the message has been signed
+ // and can be returned to the dapp
+ this.personalMessageManager.setMsgStatusSigned(msgId, rawSig)
+ return this.getState()
+ })
+ }
+
+ /**
+ * @param {} msgParams
+ */
+ signTypedMessage (msgParams) {
+ log.info('MetaMaskController - signTypedMessage')
+ const msgId = msgParams.metamaskId
+ // sets the status op the message to 'approved'
+ // and removes the metamaskId for signing
+ return this.typedMessageManager.approveMessage(msgParams)
+ .then((cleanMsgParams) => {
+ // signs the message
+ return this.keyringController.signTypedMessage(cleanMsgParams)
+ })
+ .then((rawSig) => {
+ // tells the listener that the message has been signed
+ // and can be returned to the dapp
+ this.typedMessageManager.setMsgStatusSigned(msgId, rawSig)
+ return this.getState()
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+ // Account Restauration
+
+ /**
+ * ?
+ *
+ * @param {} migratorOutput
+ */
+ restoreOldVaultAccounts (migratorOutput) {
+ const { serialized } = migratorOutput
+ return this.keyringController.restoreKeyring(serialized)
+ .then(() => migratorOutput)
+ }
+
+ /**
+ * ?
+ *
+ * @param {} migratorOutput
+ */
+ restoreOldLostAccounts (migratorOutput) {
+ const { lostAccounts } = migratorOutput
+ if (lostAccounts) {
+ this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
+ return this.importLostAccounts(migratorOutput)
+ }
+ return Promise.resolve(migratorOutput)
+ }
+
+ /**
+ * Import (lost) Accounts
+ *
+ * @param {Object} {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,
+ })
+ }
+
+//=============================================================================
+// END (VAULT / KEYRING RELATED METHODS)
+//=============================================================================
+
+//
+
+//=============================================================================
+// MESSAGES
+//=============================================================================
async retryTransaction (txId, cb) {
await this.txController.retryTransaction(txId)
@@ -730,85 +817,13 @@ module.exports = class MetamaskController extends EventEmitter {
})
}
- signMessage (msgParams, cb) {
- log.info('MetaMaskController - signMessage')
- const msgId = msgParams.metamaskId
-
- // sets the status op the message to 'approved'
- // and removes the metamaskId for signing
- return this.messageManager.approveMessage(msgParams)
- .then((cleanMsgParams) => {
- // signs the message
- return this.keyringController.signMessage(cleanMsgParams)
- })
- .then((rawSig) => {
- // tells the listener that the message has been signed
- // and can be returned to the dapp
- this.messageManager.setMsgStatusSigned(msgId, rawSig)
- return this.getState()
- })
- }
-
cancelMessage (msgId, cb) {
const messageManager = this.messageManager
messageManager.rejectMsg(msgId)
if (cb && typeof cb === 'function') {
cb(null, this.getState())
}
- }
-
- // Prefixed Style Message Signing Methods:
- approvePersonalMessage (msgParams, cb) {
- const msgId = this.personalMessageManager.addUnapprovedMessage(msgParams)
- this.sendUpdate()
- this.opts.showUnconfirmedMessage()
- this.personalMessageManager.once(`${msgId}:finished`, (data) => {
- switch (data.status) {
- case 'signed':
- return cb(null, data.rawSig)
- case 'rejected':
- return cb(new Error('MetaMask Message Signature: User denied transaction signature.'))
- default:
- return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
- }
- })
- }
-
- signPersonalMessage (msgParams) {
- log.info('MetaMaskController - signPersonalMessage')
- const msgId = msgParams.metamaskId
- // sets the status op the message to 'approved'
- // and removes the metamaskId for signing
- return this.personalMessageManager.approveMessage(msgParams)
- .then((cleanMsgParams) => {
- // signs the message
- return this.keyringController.signPersonalMessage(cleanMsgParams)
- })
- .then((rawSig) => {
- // tells the listener that the message has been signed
- // and can be returned to the dapp
- this.personalMessageManager.setMsgStatusSigned(msgId, rawSig)
- return this.getState()
- })
- }
-
- signTypedMessage (msgParams) {
- log.info('MetaMaskController - signTypedMessage')
- const msgId = msgParams.metamaskId
- // sets the status op the message to 'approved'
- // and removes the metamaskId for signing
- return this.typedMessageManager.approveMessage(msgParams)
- .then((cleanMsgParams) => {
- // signs the message
- return this.keyringController.signTypedMessage(cleanMsgParams)
- })
- .then((rawSig) => {
- // tells the listener that the message has been signed
- // and can be returned to the dapp
- this.typedMessageManager.setMsgStatusSigned(msgId, rawSig)
- return this.getState()
- })
- }
+ }
cancelPersonalMessage (msgId, cb) {
const messageManager = this.personalMessageManager
@@ -844,36 +859,130 @@ module.exports = class MetamaskController extends EventEmitter {
cb()
}
- restoreOldVaultAccounts (migratorOutput) {
- const { serialized } = migratorOutput
- return this.keyringController.restoreKeyring(serialized)
- .then(() => migratorOutput)
- }
+//=============================================================================
+// SETUP
+//=============================================================================
- restoreOldLostAccounts (migratorOutput) {
- const { lostAccounts } = migratorOutput
- if (lostAccounts) {
- this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
- return this.importLostAccounts(migratorOutput)
+ setupUntrustedCommunication (connectionStream, originDomain) {
+ // Check if new connection is blacklisted
+ if (this.blacklistController.checkForPhishing(originDomain)) {
+ log.debug('MetaMask - sending phishing warning for', originDomain)
+ this.sendPhishingWarning(connectionStream, originDomain)
+ return
}
- return Promise.resolve(migratorOutput)
+
+ // setup multiplexing
+ const mux = setupMultiplex(connectionStream)
+ // connect features
+ this.setupProviderConnection(mux.createStream('provider'), originDomain)
+ this.setupPublicConfig(mux.createStream('publicConfig'))
}
- // 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,
+ setupTrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ const mux = setupMultiplex(connectionStream)
+ // connect features
+ this.setupControllerConnection(mux.createStream('controller'))
+ this.setupProviderConnection(mux.createStream('provider'), originDomain)
+ }
+
+ sendPhishingWarning (connectionStream, hostname) {
+ const mux = setupMultiplex(connectionStream)
+ const phishingStream = mux.createStream('phishing')
+ phishingStream.write({ hostname })
+ }
+
+ setupControllerConnection (outStream) {
+ const api = this.getApi()
+ const dnode = Dnode(api)
+ pump(
+ outStream,
+ dnode,
+ outStream,
+ (err) => {
+ if (err) log.error(err)
+ }
+ )
+ dnode.on('remote', (remote) => {
+ // push updates to popup
+ const sendUpdate = remote.sendUpdate.bind(remote)
+ this.on('update', sendUpdate)
})
}
- //
- // config
- //
+ setupProviderConnection (outStream, origin) {
+ // setup json rpc engine stack
+ const engine = new RpcEngine()
+
+ // create filter polyfill middleware
+ const filterMiddleware = createFilterMiddleware({
+ provider: this.provider,
+ blockTracker: this.provider._blockTracker,
+ })
+
+ engine.push(createOriginMiddleware({ origin }))
+ engine.push(createLoggerMiddleware({ origin }))
+ engine.push(filterMiddleware)
+ engine.push(createProviderMiddleware({ provider: this.provider }))
+
+ // setup connection
+ const providerStream = createEngineStream({ engine })
+ pump(
+ outStream,
+ providerStream,
+ outStream,
+ (err) => {
+ // cleanup filter polyfill middleware
+ filterMiddleware.destroy()
+ if (err) log.error(err)
+ }
+ )
+ }
+
+ setupPublicConfig (outStream) {
+ pump(
+ asStream(this.publicConfigStore),
+ outStream,
+ (err) => {
+ if (err) log.error(err)
+ }
+ )
+ }
+
+ privateSendUpdate () {
+ this.emit('update', this.getState())
+ }
+
+ getGasPrice () {
+ const { recentBlocksController } = this
+ const { recentBlocks } = recentBlocksController.store.getState()
+
+ // Return 1 gwei if no blocks have been observed:
+ if (recentBlocks.length === 0) {
+ return '0x' + GWEI_BN.toString(16)
+ }
+
+ const lowestPrices = recentBlocks.map((block) => {
+ if (!block.gasPrices || block.gasPrices.length < 1) {
+ return GWEI_BN
+ }
+ return block.gasPrices
+ .map(hexPrefix => hexPrefix.substr(2))
+ .map(hex => new BN(hex, 16))
+ .sort((a, b) => {
+ return a.gt(b) ? 1 : -1
+ })[0]
+ })
+ .map(number => number.div(GWEI_BN).toNumber())
+
+ const percentileNum = percentile(50, lowestPrices)
+ const percentileNumBn = new BN(percentileNum)
+ return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
+ }
+
+//=============================================================================
+// CONFIG
+//=============================================================================
// Log blocks
diff --git a/app/scripts/migrations/README.md b/app/scripts/migrations/README.md
new file mode 100644
index 000000000..3a67b08e1
--- /dev/null
+++ b/app/scripts/migrations/README.md
@@ -0,0 +1,5 @@
+# Migrations
+
+Data (user data, config files etc.) is migrated from one version to another.
+
+Migrations are called by {} from {} during {}. \ No newline at end of file