From 9ed3c676ecbce23f56cd004496c881425dd302eb Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 24 Jun 2016 13:05:21 -0700 Subject: 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. --- app/scripts/background.js | 300 +++++++--------------------------------------- 1 file changed, 46 insertions(+), 254 deletions(-) (limited to 'app/scripts/background.js') 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 () {} -- cgit From b2b3f4b26bbaaf978b454afe18e9116ac8a693d4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 24 Jun 2016 15:52:56 -0700 Subject: Improve refactor Replace config-manager-singleton with one that is instantiated within the `background-controller`, and takes its persistence callbacks from its instantiated options. Replaced class getters with more familiar get___() functions. --- app/scripts/background.js | 62 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) (limited to 'app/scripts/background.js') diff --git a/app/scripts/background.js b/app/scripts/background.js index d90b047e7..83d8d0258 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -5,16 +5,20 @@ const PortStream = require('./lib/port-stream.js') 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 BackgroundController = require('./background-controller') +const STORAGE_KEY = 'metamask-config' + const controller = new BackgroundController({ + // User confirmation callbacks: showUnconfirmedMessage, unlockAccountMessage, showUnconfirmedTx, + // Persistence Methods: + setData, + loadData, }) const idStore = controller.idStore @@ -82,7 +86,7 @@ function setupTrustedCommunication (connectionStream, originDomain) { function setupControllerConnection (stream) { controller.stream = stream - var api = controller.api + var api = controller.getApi() var dnode = Dnode(api) stream.pipe(dnode).pipe(stream) dnode.on('remote', function() { @@ -106,7 +110,7 @@ idStore.on('update', updateBadge) function updateBadge (state) { var label = '' - var unconfTxs = configManager.unconfirmedTxs() + var unconfTxs = controller.configManager.unconfirmedTxs() var unconfTxLen = Object.keys(unconfTxs).length var unconfMsgs = messageManager.unconfirmedMsgs() var unconfMsgLen = Object.keys(unconfMsgs).length @@ -118,4 +122,54 @@ function updateBadge (state) { chrome.browserAction.setBadgeBackgroundColor({ color: '#506F8B' }) } +function loadData () { + var oldData = getOldStyleData() + var newData + try { + newData = JSON.parse(window.localStorage[STORAGE_KEY]) + } catch (e) {} + + var data = extend({ + meta: { + version: 0, + }, + data: { + config: { + provider: { + type: 'testnet', + }, + }, + }, + }, oldData || null, newData || null) + return data +} + +function getOldStyleData () { + var config, wallet, seedWords + + var result = { + meta: { version: 0 }, + data: {}, + } + + try { + config = JSON.parse(window.localStorage['config']) + result.data.config = config + } catch (e) {} + try { + wallet = JSON.parse(window.localStorage['lightwallet']) + result.data.wallet = wallet + } catch (e) {} + try { + seedWords = window.localStorage['seedWords'] + result.data.seedWords = seedWords + } catch (e) {} + + return result +} + +function setData (data) { + window.localStorage[STORAGE_KEY] = JSON.stringify(data) +} + function noop () {} -- cgit From fa7e4665594525256b846de93bdd46115a3a53a7 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 24 Jun 2016 15:55:11 -0700 Subject: Rename background-controller metamask-controller --- app/scripts/background.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/scripts/background.js') diff --git a/app/scripts/background.js b/app/scripts/background.js index 83d8d0258..d23951015 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -7,11 +7,11 @@ const createTxNotification = require('./lib/notifications.js').createTxNotificat const createMsgNotification = require('./lib/notifications.js').createMsgNotification const messageManager = require('./lib/message-manager') const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex -const BackgroundController = require('./background-controller') +const MetamaskController = require('./metamask-controller') const STORAGE_KEY = 'metamask-config' -const controller = new BackgroundController({ +const controller = new MetamaskController({ // User confirmation callbacks: showUnconfirmedMessage, unlockAccountMessage, -- cgit From 122018a96a1b2b6d9b7eefe150ff76a9de69bdd7 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 24 Jun 2016 16:13:27 -0700 Subject: Fixed tests --- app/scripts/background.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app/scripts/background.js') diff --git a/app/scripts/background.js b/app/scripts/background.js index d23951015..09cdffecd 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1,4 +1,5 @@ const urlUtil = require('url') +const extend = require('xtend') const Dnode = require('dnode') const eos = require('end-of-stream') const PortStream = require('./lib/port-stream.js') @@ -22,7 +23,7 @@ const controller = new MetamaskController({ }) const idStore = controller.idStore -function unlockAccountMessage() { +function unlockAccountMessage () { createUnlockRequestNotification({ title: 'Account Unlock Request', }) @@ -37,7 +38,7 @@ function showUnconfirmedMessage (msgParams, msgId) { }) } -function showUnconfirmedTx(txParams, txData, onTxDoneCb) { +function showUnconfirmedTx (txParams, txData, onTxDoneCb) { createTxNotification({ title: 'New Unsigned Transaction', txParams: txParams, @@ -89,7 +90,7 @@ function setupControllerConnection (stream) { var api = controller.getApi() var dnode = Dnode(api) stream.pipe(dnode).pipe(stream) - dnode.on('remote', function() { + dnode.on('remote', () => { // push updates to popup controller.ethStore.on('update', controller.sendUpdate) idStore.on('update', controller.sendUpdate) @@ -99,7 +100,6 @@ function setupControllerConnection (stream) { controller.ethStore.removeListener('update', controller.sendUpdate) }) }) - } // -- cgit From d3e0e7fe45a1dc2159b26155b974ced16548ea44 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 24 Jun 2016 16:46:18 -0700 Subject: Manually debugged refactor --- app/scripts/background.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'app/scripts/background.js') diff --git a/app/scripts/background.js b/app/scripts/background.js index 09cdffecd..97e3269ba 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -90,14 +90,15 @@ function setupControllerConnection (stream) { var api = controller.getApi() var dnode = Dnode(api) stream.pipe(dnode).pipe(stream) - dnode.on('remote', () => { + dnode.on('remote', (remote) => { // push updates to popup - controller.ethStore.on('update', controller.sendUpdate) - idStore.on('update', controller.sendUpdate) + controller.ethStore.on('update', controller.sendUpdate.bind(controller)) + controller.remote = remote + idStore.on('update', controller.sendUpdate.bind(controller)) // teardown on disconnect eos(stream, () => { - controller.ethStore.removeListener('update', controller.sendUpdate) + controller.ethStore.removeListener('update', controller.sendUpdate.bind(controller)) }) }) } -- cgit