aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2016-06-25 04:05:21 +0800
committerDan Finlay <dan@danfinlay.com>2016-06-25 04:05:21 +0800
commit9ed3c676ecbce23f56cd004496c881425dd302eb (patch)
tree602b9e9ac695f44494099b026df201beac94ad2e
parentcbcc0438cb673b57d77afd3aa0a2015e84201453 (diff)
downloadtangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.tar
tangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.tar.gz
tangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.tar.bz2
tangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.tar.lz
tangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.tar.xz
tangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.tar.zst
tangerine-wallet-browser-9ed3c676ecbce23f56cd004496c881425dd302eb.zip
Refactored background.js controller apart from chrome + transport
Still needs linting fixes, probably bugs, this commit should be used as a draft of what a separation of these concerns might look like.
-rw-r--r--app/scripts/background-controller.js255
-rw-r--r--app/scripts/background.js300
2 files changed, 301 insertions, 254 deletions
diff --git a/app/scripts/background-controller.js b/app/scripts/background-controller.js
new file mode 100644
index 000000000..e5814e84c
--- /dev/null
+++ b/app/scripts/background-controller.js
@@ -0,0 +1,255 @@
+const extend = require('xtend')
+const EthStore = require('eth-store')
+const MetaMaskProvider = require('web3-provider-engine/zero.js')
+const IdentityStore = require('./lib/idStore')
+const configManager = require('./lib/config-manager-singleton')
+const messageManager = require('./lib/message-manager')
+const HostStore = require('./lib/remote-store.js').HostStore
+const Web3 = require('web3')
+
+
+module.exports = BackgroundController
+
+class BackgroundController {
+
+ constructor (opts) {
+ this.idStore = new IdentityStore()
+ this.configManager = configManager
+ this.messageManager = messageManager
+ this.provider = this.initializeProvider(opts)
+ this.ethStore = new EthStore(this.provider)
+ this.idStore.setStore(this.ethStore)
+ this.publicConfigStore = this.initPublicConfigStore()
+ }
+
+ get state () {
+ return extend(
+ this.ethStore.getState(),
+ this.idStore.getState(),
+ this.configManager.getConfig()
+ )
+ }
+
+ get api () {
+ const idStore = this.idStore
+
+ return {
+ getState: function (cb) { cb(null, this.state) },
+ setRpcTarget: setRpcTarget,
+ setProviderType: setProviderType,
+ useEtherscanProvider: useEtherscanProvider,
+ agreeToDisclaimer: agreeToDisclaimer,
+ // forward directly to idStore
+ createNewVault: idStore.createNewVault.bind(idStore),
+ recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
+ submitPassword: idStore.submitPassword.bind(idStore),
+ setSelectedAddress: idStore.setSelectedAddress.bind(idStore),
+ approveTransaction: idStore.approveTransaction.bind(idStore),
+ cancelTransaction: idStore.cancelTransaction.bind(idStore),
+ signMessage: idStore.signMessage.bind(idStore),
+ cancelMessage: idStore.cancelMessage.bind(idStore),
+ setLocked: idStore.setLocked.bind(idStore),
+ clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore),
+ exportAccount: idStore.exportAccount.bind(idStore),
+ revealAccount: idStore.revealAccount.bind(idStore),
+ saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
+ tryPassword: idStore.tryPassword.bind(idStore),
+ recoverSeed: idStore.recoverSeed.bind(idStore),
+ }
+ }
+
+ setupProviderConnection (stream, originDomain) {
+ stream.on('data', this.onRpcRequest.bind(this, stream, originDomain))
+ }
+
+ onRpcRequest (stream, originDomain, request) {
+ var payloads = Array.isArray(request) ? request : [request]
+ payloads.forEach(function (payload) {
+ // Append origin to rpc payload
+ payload.origin = originDomain
+ // Append origin to signature request
+ if (payload.method === 'eth_sendTransaction') {
+ payload.params[0].origin = originDomain
+ } else if (payload.method === 'eth_sign') {
+ payload.params.push({ origin: originDomain })
+ }
+ })
+
+ // handle rpc request
+ this.provider.sendAsync(request, function onPayloadHandled (err, response) {
+ if (err) {
+ return logger(err)
+ }
+ logger(null, request, response)
+ try {
+ stream.write(response)
+ } catch (err) {
+ logger(err)
+ }
+ })
+
+ function logger (err, request, response) {
+ if (err) return console.error(err.stack)
+ if (!request.isMetamaskInternal) {
+ console.log(`RPC (${originDomain}):`, request, '->', response)
+ if (response.error) console.error('Error in RPC response:\n' + response.error.message)
+ }
+ }
+ }
+
+ sendUpdate () {
+ this.remote.sendUpdate(this.state)
+ }
+
+ initializeProvider (opts) {
+ const idStore = this.idStore
+
+ var providerOpts = {
+ rpcUrl: configManager.getCurrentRpcAddress(),
+ // account mgmt
+ getAccounts: function (cb) {
+ var selectedAddress = idStore.getSelectedAddress()
+ var result = selectedAddress ? [selectedAddress] : []
+ cb(null, result)
+ },
+ // tx signing
+ approveTransaction: this.newUnsignedTransaction,
+ signTransaction: idStore.signTransaction.bind(idStore),
+ // msg signing
+ approveMessage: this.newUnsignedMessage,
+ signMessage: idStore.signMessage.bind(idStore),
+ }
+
+ var provider = MetaMaskProvider(providerOpts)
+ var web3 = new Web3(provider)
+ idStore.web3 = web3
+ idStore.getNetwork()
+
+ provider.on('block', this.processBlock)
+ provider.on('error', idStore.getNetwork.bind(idStore))
+
+ return provider
+ }
+
+ initPublicConfigStore () {
+ // get init state
+ var initPublicState = extend(
+ idStoreToPublic(this.idStore.getState()),
+ configToPublic(this.configManager.getConfig())
+ )
+
+ var publicConfigStore = new HostStore(initPublicState)
+
+ // subscribe to changes
+ this.configManager.subscribe(function (state) {
+ storeSetFromObj(publicConfigStore, configToPublic(state))
+ })
+ this.idStore.on('update', function (state) {
+ storeSetFromObj(publicConfigStore, idStoreToPublic(state))
+ })
+
+ // idStore substate
+ function idStoreToPublic (state) {
+ return {
+ selectedAddress: state.selectedAddress,
+ }
+ }
+ // config substate
+ function configToPublic (state) {
+ return {
+ provider: state.provider,
+ }
+ }
+ // dump obj into store
+ function storeSetFromObj (store, obj) {
+ Object.keys(obj).forEach(function (key) {
+ store.set(key, obj[key])
+ })
+ }
+
+ return publicConfigStore
+ }
+
+ newUnsignedTransaction (txParams, onTxDoneCb) {
+ const idStore = this.idStore
+ var state = idStore.getState()
+
+ // It's locked
+ if (!state.isUnlocked) {
+ this.opts.unlockAccountMessage()
+ idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
+
+ // It's unlocked
+ } else {
+ idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
+ if (err) return onTxDoneCb(err)
+ this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
+ })
+ }
+ }
+
+ newUnsignedMessage(msgParams, cb) {
+ var state = this.idStore.getState()
+ if (!state.isUnlocked) {
+ this.opts.unlockAccountMessage()
+ } else {
+ this.addUnconfirmedMsg(msgParams, cb)
+ }
+ }
+
+ addUnconfirmedMessage (msgParams, cb) {
+ const idStore = this.idStore
+ const msgId = idStore.addUnconfirmedMessage(msgParams, cb)
+ this.opts.showUnconfirmedMessage(msgParams, msgId)
+ }
+
+ setupPublicConfig (stream) {
+ var storeStream = publicConfigStore.createStream()
+ stream.pipe(storeStream).pipe(stream)
+ }
+
+ // Log blocks
+ processBlock (block) {
+ console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
+ this.verifyNetwork()
+ }
+
+ verifyNetwork () {
+ // Check network when restoring connectivity:
+ if (this.idStore._currentState.network === 'loading') {
+ this.idStore.getNetwork()
+ }
+ }
+}
+
+// config
+//
+
+function agreeToDisclaimer (cb) {
+ try {
+ configManager.setConfirmed(true)
+ cb()
+ } catch (e) {
+ cb(e)
+ }
+}
+
+// called from popup
+function setRpcTarget (rpcTarget) {
+ configManager.setRpcTarget(rpcTarget)
+ chrome.runtime.reload()
+ idStore.getNetwork()
+}
+
+function setProviderType (type) {
+ configManager.setProviderType(type)
+ chrome.runtime.reload()
+ idStore.getNetwork()
+}
+
+function useEtherscanProvider () {
+ configManager.useEtherscanProvider()
+ chrome.runtime.reload()
+}
+
+function noop () {}
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 6934e9d3e..d90b047e7 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -1,19 +1,46 @@
const urlUtil = require('url')
const Dnode = require('dnode')
const eos = require('end-of-stream')
-const extend = require('xtend')
-const EthStore = require('eth-store')
-const MetaMaskProvider = require('web3-provider-engine/zero.js')
const PortStream = require('./lib/port-stream.js')
-const IdentityStore = require('./lib/idStore')
const createUnlockRequestNotification = require('./lib/notifications.js').createUnlockRequestNotification
const createTxNotification = require('./lib/notifications.js').createTxNotification
const createMsgNotification = require('./lib/notifications.js').createMsgNotification
const configManager = require('./lib/config-manager-singleton')
const messageManager = require('./lib/message-manager')
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
-const HostStore = require('./lib/remote-store.js').HostStore
-const Web3 = require('web3')
+
+const BackgroundController = require('./background-controller')
+
+const controller = new BackgroundController({
+ showUnconfirmedMessage,
+ unlockAccountMessage,
+ showUnconfirmedTx,
+})
+const idStore = controller.idStore
+
+function unlockAccountMessage() {
+ createUnlockRequestNotification({
+ title: 'Account Unlock Request',
+ })
+}
+
+function showUnconfirmedMessage (msgParams, msgId) {
+ createMsgNotification({
+ title: 'New Unsigned Message',
+ msgParams: msgParams,
+ confirm: idStore.approveMessage.bind(idStore, msgId, noop),
+ cancel: idStore.cancelMessage.bind(idStore, msgId),
+ })
+}
+
+function showUnconfirmedTx(txParams, txData, onTxDoneCb) {
+ createTxNotification({
+ title: 'New Unsigned Transaction',
+ txParams: txParams,
+ confirm: idStore.approveTransaction.bind(idStore, txData.id, noop),
+ cancel: idStore.cancelTransaction.bind(idStore, txData.id),
+ })
+}
//
// connect to other contexts
@@ -37,8 +64,8 @@ function setupUntrustedCommunication (connectionStream, originDomain) {
// setup multiplexing
var mx = setupMultiplex(connectionStream)
// connect features
- setupProviderConnection(mx.createStream('provider'), originDomain)
- setupPublicConfig(mx.createStream('publicConfig'))
+ controller.setupProviderConnection(mx.createStream('provider'), originDomain)
+ controller.setupPublicConfig(mx.createStream('publicConfig'))
}
function setupTrustedCommunication (connectionStream, originDomain) {
@@ -46,182 +73,29 @@ function setupTrustedCommunication (connectionStream, originDomain) {
var mx = setupMultiplex(connectionStream)
// connect features
setupControllerConnection(mx.createStream('controller'))
- setupProviderConnection(mx.createStream('provider'), originDomain)
-}
-
-//
-// state and network
-//
-
-var idStore = new IdentityStore()
-
-var providerOpts = {
- rpcUrl: configManager.getCurrentRpcAddress(),
- // account mgmt
- getAccounts: function (cb) {
- var selectedAddress = idStore.getSelectedAddress()
- var result = selectedAddress ? [selectedAddress] : []
- cb(null, result)
- },
- // tx signing
- approveTransaction: newUnsignedTransaction,
- signTransaction: idStore.signTransaction.bind(idStore),
- // msg signing
- approveMessage: newUnsignedMessage,
- signMessage: idStore.signMessage.bind(idStore),
-}
-var provider = MetaMaskProvider(providerOpts)
-var web3 = new Web3(provider)
-idStore.web3 = web3
-idStore.getNetwork()
-
-// log new blocks
-provider.on('block', function (block) {
- console.log('BLOCK CHANGED:', '#' + block.number.toString('hex'), '0x' + block.hash.toString('hex'))
-
- // Check network when restoring connectivity:
- if (idStore._currentState.network === 'loading') {
- idStore.getNetwork()
- }
-})
-
-provider.on('error', idStore.getNetwork.bind(idStore))
-
-var ethStore = new EthStore(provider)
-idStore.setStore(ethStore)
-
-function getState () {
- var state = extend(
- ethStore.getState(),
- idStore.getState(),
- configManager.getConfig()
- )
- return state
-}
-
-//
-// public store
-//
-
-// get init state
-var initPublicState = extend(
- idStoreToPublic(idStore.getState()),
- configToPublic(configManager.getConfig())
-)
-
-var publicConfigStore = new HostStore(initPublicState)
-
-// subscribe to changes
-configManager.subscribe(function (state) {
- storeSetFromObj(publicConfigStore, configToPublic(state))
-})
-idStore.on('update', function (state) {
- storeSetFromObj(publicConfigStore, idStoreToPublic(state))
-})
-
-// idStore substate
-function idStoreToPublic (state) {
- return {
- selectedAddress: state.selectedAddress,
- }
-}
-// config substate
-function configToPublic (state) {
- return {
- provider: state.provider,
- }
-}
-// dump obj into store
-function storeSetFromObj (store, obj) {
- Object.keys(obj).forEach(function (key) {
- store.set(key, obj[key])
- })
+ controller.setupProviderConnection(mx.createStream('provider'), originDomain)
}
//
// remote features
//
-function setupPublicConfig (stream) {
- var storeStream = publicConfigStore.createStream()
- stream.pipe(storeStream).pipe(stream)
-}
-
-function setupProviderConnection (stream, originDomain) {
- // decorate all payloads with origin domain
- stream.on('data', function onRpcRequest (request) {
- var payloads = Array.isArray(request) ? request : [request]
- payloads.forEach(function (payload) {
- // Append origin to rpc payload
- payload.origin = originDomain
- // Append origin to signature request
- if (payload.method === 'eth_sendTransaction') {
- payload.params[0].origin = originDomain
- } else if (payload.method === 'eth_sign') {
- payload.params.push({ origin: originDomain })
- }
- })
- // handle rpc request
- provider.sendAsync(request, function onPayloadHandled (err, response) {
- if (err) {
- return logger(err)
- }
- logger(null, request, response)
- try {
- stream.write(response)
- } catch (err) {
- logger(err)
- }
- })
- })
-
- function logger (err, request, response) {
- if (err) return console.error(err.stack)
- if (!request.isMetamaskInternal) {
- console.log(`RPC (${originDomain}):`, request, '->', response)
- if (response.error) console.error('Error in RPC response:\n' + response.error.message)
- }
- }
-}
-
function setupControllerConnection (stream) {
- var dnode = Dnode({
- getState: function (cb) { cb(null, getState()) },
- setRpcTarget: setRpcTarget,
- setProviderType: setProviderType,
- useEtherscanProvider: useEtherscanProvider,
- agreeToDisclaimer: agreeToDisclaimer,
- // forward directly to idStore
- createNewVault: idStore.createNewVault.bind(idStore),
- recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
- submitPassword: idStore.submitPassword.bind(idStore),
- setSelectedAddress: idStore.setSelectedAddress.bind(idStore),
- approveTransaction: idStore.approveTransaction.bind(idStore),
- cancelTransaction: idStore.cancelTransaction.bind(idStore),
- signMessage: idStore.signMessage.bind(idStore),
- cancelMessage: idStore.cancelMessage.bind(idStore),
- setLocked: idStore.setLocked.bind(idStore),
- clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore),
- exportAccount: idStore.exportAccount.bind(idStore),
- revealAccount: idStore.revealAccount.bind(idStore),
- saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
- tryPassword: idStore.tryPassword.bind(idStore),
- recoverSeed: idStore.recoverSeed.bind(idStore),
- })
+ controller.stream = stream
+ var api = controller.api
+ var dnode = Dnode(api)
stream.pipe(dnode).pipe(stream)
- dnode.on('remote', function (remote) {
+ dnode.on('remote', function() {
// push updates to popup
- ethStore.on('update', sendUpdate)
- idStore.on('update', sendUpdate)
+ controller.ethStore.on('update', controller.sendUpdate)
+ idStore.on('update', controller.sendUpdate)
+
// teardown on disconnect
- eos(stream, function unsubscribe () {
- ethStore.removeListener('update', sendUpdate)
+ eos(stream, () => {
+ controller.ethStore.removeListener('update', controller.sendUpdate)
})
- function sendUpdate () {
- var state = getState()
- remote.sendUpdate(state)
- }
})
+
}
//
@@ -244,86 +118,4 @@ function updateBadge (state) {
chrome.browserAction.setBadgeBackgroundColor({ color: '#506F8B' })
}
-//
-// Add unconfirmed Tx + Msg
-//
-
-function newUnsignedTransaction (txParams, onTxDoneCb) {
- var state = idStore.getState()
- if (!state.isUnlocked) {
- createUnlockRequestNotification({
- title: 'Account Unlock Request',
- })
- idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
- } else {
- addUnconfirmedTx(txParams, onTxDoneCb)
- }
-}
-
-function newUnsignedMessage (msgParams, cb) {
- var state = idStore.getState()
- if (!state.isUnlocked) {
- createUnlockRequestNotification({
- title: 'Account Unlock Request',
- })
- } else {
- addUnconfirmedMsg(msgParams, cb)
- }
-}
-
-function addUnconfirmedTx (txParams, onTxDoneCb) {
- idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, function (err, txData) {
- if (err) return onTxDoneCb(err)
- createTxNotification({
- title: 'New Unsigned Transaction',
- txParams: txParams,
- confirm: idStore.approveTransaction.bind(idStore, txData.id, noop),
- cancel: idStore.cancelTransaction.bind(idStore, txData.id),
- })
- })
-}
-
-function addUnconfirmedMsg (msgParams, cb) {
- var msgId = idStore.addUnconfirmedMessage(msgParams, cb)
- createMsgNotification({
- title: 'New Unsigned Message',
- msgParams: msgParams,
- confirm: idStore.approveMessage.bind(idStore, msgId, noop),
- cancel: idStore.cancelMessage.bind(idStore, msgId),
- })
-}
-
-//
-// config
-//
-
-function agreeToDisclaimer (cb) {
- try {
- configManager.setConfirmed(true)
- cb()
- } catch (e) {
- cb(e)
- }
-}
-
-// called from popup
-function setRpcTarget (rpcTarget) {
- configManager.setRpcTarget(rpcTarget)
- chrome.runtime.reload()
- idStore.getNetwork()
-}
-
-function setProviderType (type) {
- configManager.setProviderType(type)
- chrome.runtime.reload()
- idStore.getNetwork()
-}
-
-function useEtherscanProvider () {
- configManager.useEtherscanProvider()
- chrome.runtime.reload()
-}
-
-// util
-
function noop () {}