aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorkumavis <kumavis@users.noreply.github.com>2018-03-21 07:47:04 +0800
committerGitHub <noreply@github.com>2018-03-21 07:47:04 +0800
commit112a9443ee7d8793eff110ecc07edd06dba82f5e (patch)
treefd886cec879c11a19f943c721aeb3755cf3a0b27 /app
parent1a60094d501d8b102127c2cfc7b2ff31070feeb1 (diff)
parente1d6398b0fed39e579c0383e34236b30d3154295 (diff)
downloadtangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.tar
tangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.tar.gz
tangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.tar.bz2
tangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.tar.lz
tangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.tar.xz
tangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.tar.zst
tangerine-wallet-browser-112a9443ee7d8793eff110ecc07edd06dba82f5e.zip
Merge pull request #3585 from lazaridiscom/laz/i3568
[WIP] document/rearrange metamask-controller.js, re #3568
Diffstat (limited to 'app')
-rw-r--r--app/scripts/metamask-controller.js599
1 files changed, 354 insertions, 245 deletions
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