diff options
author | kumavis <kumavis@users.noreply.github.com> | 2016-04-16 04:06:28 +0800 |
---|---|---|
committer | kumavis <kumavis@users.noreply.github.com> | 2016-04-16 04:06:28 +0800 |
commit | 83b8741bbb335ad1629b72180616835dbb3f5433 (patch) | |
tree | b8adfc80551c1032dc6fc43aa3a5055169446143 /app | |
parent | 81fc1e5f7dc958af40d8113b80c8cfb38c2016ef (diff) | |
parent | d6114292d0c05b42b1d28d75baad8af0c06509a1 (diff) | |
download | tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.tar tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.tar.gz tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.tar.bz2 tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.tar.lz tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.tar.xz tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.tar.zst tangerine-wallet-browser-83b8741bbb335ad1629b72180616835dbb3f5433.zip |
Merge pull request #113 from MetaMask/i53
publicConfigStore for sync provider and selected address
Diffstat (limited to 'app')
-rw-r--r-- | app/scripts/background.js | 96 | ||||
-rw-r--r-- | app/scripts/inpage.js | 80 | ||||
-rw-r--r-- | app/scripts/lib/config-manager.js | 27 | ||||
-rw-r--r-- | app/scripts/lib/obj-multiplex.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/remote-store.js | 97 | ||||
-rw-r--r-- | app/scripts/lib/stream-utils.js | 16 | ||||
-rw-r--r-- | app/scripts/popup.js | 22 |
7 files changed, 263 insertions, 77 deletions
diff --git a/app/scripts/background.js b/app/scripts/background.js index f3dd8cbb6..db4927083 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -9,8 +9,8 @@ const MetaMaskProvider = require('./lib/zero.js') const IdentityStore = require('./lib/idStore') const createTxNotification = require('./lib/tx-notification.js') const configManager = require('./lib/config-manager-singleton') -const jsonParseStream = require('./lib/stream-utils.js').jsonParseStream -const jsonStringifyStream = require('./lib/stream-utils.js').jsonStringifyStream +const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex +const HostStore = require('./lib/remote-store.js').HostStore // // connect to other contexts @@ -22,15 +22,27 @@ function connectRemote(remotePort){ var portStream = new PortStream(remotePort) if (isMetaMaskInternalProcess) { // communication with popup - handleInternalCommunication(portStream) + setupTrustedCommunication(portStream) } else { // communication with page - handleEthRpcRequestStream(portStream) + setupUntrustedCommunication(portStream) } } -function handleEthRpcRequestStream(stream){ - stream.on('data', onRpcRequest.bind(null, stream)) +function setupUntrustedCommunication(connectionStream){ + // setup multiplexing + var mx = setupMultiplex(connectionStream) + // connect features + setupProviderConnection(mx.createStream('provider')) + setupPublicConfig(mx.createStream('publicConfig')) +} + +function setupTrustedCommunication(connectionStream){ + // setup multiplexing + var mx = setupMultiplex(connectionStream) + // connect features + setupControllerConnection(mx.createStream('controller')) + setupProviderConnection(mx.createStream('provider')) } // @@ -68,6 +80,47 @@ function getState(){ 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]) + }) +} + + + // handle rpc requests function onRpcRequest(remoteStream, payload){ // console.log('MetaMaskPlugin - incoming payload:', payload) @@ -84,27 +137,20 @@ function onRpcRequest(remoteStream, payload){ // -// popup integration +// remote features // -function handleInternalCommunication(portStream){ - // setup multiplexing - var mx = ObjectMultiplex() - portStream.pipe(mx).pipe(portStream) - mx.on('error', function(err) { - console.error(err) - // portStream.destroy() - }) - portStream.on('error', function(err) { - console.error(err) - mx.destroy() - }) - linkDnode(mx.createStream('dnode')) - handleEthRpcRequestStream(mx.createStream('provider')) +function setupPublicConfig(stream){ + var storeStream = publicConfigStore.createStream() + stream.pipe(storeStream).pipe(stream) +} + +function setupProviderConnection(stream){ + stream.on('data', onRpcRequest.bind(null, stream)) } -function linkDnode(stream){ - var connection = Dnode({ +function setupControllerConnection(stream){ + var dnode = Dnode({ getState: function(cb){ cb(null, getState()) }, setRpcTarget: setRpcTarget, useEtherscanProvider: useEtherscanProvider, @@ -119,8 +165,8 @@ function linkDnode(stream){ clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore), exportAccount: idStore.exportAccount.bind(idStore), }) - stream.pipe(connection).pipe(stream) - connection.on('remote', function(remote){ + stream.pipe(dnode).pipe(stream) + dnode.on('remote', function(remote){ // push updates to popup ethStore.on('update', sendUpdate) diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index 01f35d0fe..e6684cbd4 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -1,31 +1,42 @@ -const XHR = window.XMLHttpRequest - -// bring in web3 but rename on window -const Web3 = require('web3') -delete window.Web3 -window.MetamaskWeb3 = Web3 - const createPayload = require('web3-provider-engine/util/create-payload') const StreamProvider = require('./lib/stream-provider.js') const LocalMessageDuplexStream = require('./lib/local-message-stream.js') +const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex +const RemoteStore = require('./lib/remote-store.js').RemoteStore +const Web3 = require('web3') -const RPC_URL = 'https://testrpc.metamask.io/' +// rename on window +delete window.Web3 +window.MetamaskWeb3 = Web3 + +const DEFAULT_RPC_URL = 'https://rpc.metamask.io/' // // setup plugin communication // +// setup background connection var pluginStream = new LocalMessageDuplexStream({ name: 'inpage', target: 'contentscript', }) +var mx = setupMultiplex(pluginStream) +// connect features var remoteProvider = new StreamProvider() -remoteProvider.pipe(pluginStream).pipe(remoteProvider) - -pluginStream.on('error', console.error.bind(console)) +remoteProvider.pipe(mx.createStream('provider')).pipe(remoteProvider) remoteProvider.on('error', console.error.bind(console)) +var initState = JSON.parse(localStorage['MetaMask-Config'] || '{}') +var publicConfigStore = new RemoteStore(initState) +var storeStream = publicConfigStore.createStream() +storeStream.pipe(mx.createStream('publicConfig')).pipe(storeStream) + +publicConfigStore.subscribe(function(state){ + localStorage['MetaMask-Config'] = JSON.stringify(state) +}) + + // // global web3 // @@ -42,42 +53,39 @@ console.log('MetaMask - injected web3') // handle synchronous requests // -// handle accounts cache -var accountsCache = JSON.parse(localStorage['MetaMask-Accounts'] || '[]') -web3.eth.defaultAccount = accountsCache[0] - -setInterval(populateAccountsCache, 4000) -function populateAccountsCache(){ - remoteProvider.sendAsync(createPayload({ - method: 'eth_accounts', - params: [], - isMetamaskInternal: true, - }), function(err, response){ - if (err) return console.error('MetaMask - Error polling accounts') - // update localStorage - var accounts = response.result - if (accounts.toString() !== accountsCache.toString()) { - accountsCache = accounts - web3.eth.defaultAccount = accountsCache[0] - localStorage['MetaMask-Accounts'] = JSON.stringify(accounts) - } - }) -} +global.publicConfigStore = publicConfigStore + +// set web3 defaultAcount +publicConfigStore.subscribe(function(state){ + web3.eth.defaultAccount = state.selectedAddress +}) + +// setup sync http provider +var providerConfig = publicConfigStore.get('provider') || {} +var providerUrl = providerConfig.rpcTarget ? providerConfig.rpcTarget : DEFAULT_RPC_URL +var syncProvider = new Web3.providers.HttpProvider(providerUrl) +publicConfigStore.subscribe(function(state){ + if (!state.provider) return + if (!state.provider.rpcTarget || state.provider.rpcTarget === providerUrl) return + providerUrl = state.provider.rpcTarget + syncProvider = new Web3.providers.HttpProvider(providerUrl) +}) -// handle synchronous methods via standard http provider -var syncProvider = new Web3.providers.HttpProvider(RPC_URL) +// handle sync methods remoteProvider.send = function(payload){ var result = null switch (payload.method) { case 'eth_accounts': // read from localStorage - result = accountsCache + var selectedAddress = publicConfigStore.get('selectedAddress') + result = selectedAddress ? [selectedAddress] : [] break case 'eth_coinbase': // read from localStorage - result = accountsCache[0] || '0x0000000000000000000000000000000000000000' + var selectedAddress = publicConfigStore.get('selectedAddress') + result = selectedAddress || '0x0000000000000000000000000000000000000000' break // fallback to normal rpc diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index c557891fe..f024729cc 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -15,6 +15,8 @@ const migrations = require('./migrations') */ module.exports = ConfigManager function ConfigManager() { + // ConfigManager is observable and will emit updates + this._subs = [] /* The migrator exported on the config-manager * has two methods the user should be concerned with: @@ -47,6 +49,7 @@ ConfigManager.prototype.setConfig = function(config) { var data = this.migrator.getData() data.config = config this.setData(data) + this._emitUpdates(config) } ConfigManager.prototype.getConfig = function() { @@ -127,6 +130,30 @@ ConfigManager.prototype.clearWallet = function() { this.setData(data) } +ConfigManager.prototype.setData = function(data) { + this.migrator.saveData(data) +} + +// observable + +ConfigManager.prototype.subscribe = function(fn){ + this._subs.push(fn) + var unsubscribe = this.unsubscribe.bind(this, fn) + return unsubscribe +} + +ConfigManager.prototype.unsubscribe = function(fn){ + var index = this._subs.indexOf(fn) + if (index !== -1) this._subs.splice(index, 1) +} + +ConfigManager.prototype._emitUpdates = function(state){ + this._subs.forEach(function(handler){ + handler(state) + }) +} + + function loadData() { var oldData = getOldStyleData() diff --git a/app/scripts/lib/obj-multiplex.js b/app/scripts/lib/obj-multiplex.js index 333b6061f..ad1d914f8 100644 --- a/app/scripts/lib/obj-multiplex.js +++ b/app/scripts/lib/obj-multiplex.js @@ -11,7 +11,7 @@ function ObjectMultiplex(opts){ var data = chunk.data var substream = mx.streams[name] if (!substream) { - console.warn("orphaned data for stream " + name) + console.warn('orphaned data for stream ' + name) } else { substream.push(data) } diff --git a/app/scripts/lib/remote-store.js b/app/scripts/lib/remote-store.js new file mode 100644 index 000000000..2dbdde811 --- /dev/null +++ b/app/scripts/lib/remote-store.js @@ -0,0 +1,97 @@ +const Dnode = require('dnode') +const inherits = require('util').inherits + +module.exports = { + HostStore: HostStore, + RemoteStore: RemoteStore, +} + +function BaseStore(initState){ + this._state = initState || {} + this._subs = [] +} + +BaseStore.prototype.set = function(key, value){ + throw Error('Not implemented.') +} + +BaseStore.prototype.get = function(key){ + return this._state[key] +} + +BaseStore.prototype.subscribe = function(fn){ + this._subs.push(fn) + var unsubscribe = this.unsubscribe.bind(this, fn) + return unsubscribe +} + +BaseStore.prototype.unsubscribe = function(fn){ + var index = this._subs.indexOf(fn) + if (index !== -1) this._subs.splice(index, 1) +} + +BaseStore.prototype._emitUpdates = function(state){ + this._subs.forEach(function(handler){ + handler(state) + }) +} + +// +// host +// + +inherits(HostStore, BaseStore) +function HostStore(initState, opts){ + BaseStore.call(this, initState) +} + +HostStore.prototype.set = function(key, value){ + this._state[key] = value + process.nextTick(this._emitUpdates.bind(this, this._state)) +} + +HostStore.prototype.createStream = function(){ + var dnode = Dnode({ + // update: this._didUpdate.bind(this), + }) + dnode.on('remote', this._didConnect.bind(this)) + return dnode +} + +HostStore.prototype._didConnect = function(remote){ + this.subscribe(function(state){ + remote.update(state) + }) + remote.update(this._state) +} + +// +// remote +// + +inherits(RemoteStore, BaseStore) +function RemoteStore(initState, opts){ + BaseStore.call(this, initState) + this._remote = null +} + +RemoteStore.prototype.set = function(key, value){ + this._remote.set(key, value) +} + +RemoteStore.prototype.createStream = function(){ + var dnode = Dnode({ + update: this._didUpdate.bind(this), + }) + dnode.once('remote', this._didConnect.bind(this)) + return dnode +} + +RemoteStore.prototype._didConnect = function(remote){ + this._remote = remote +} + +RemoteStore.prototype._didUpdate = function(state){ + this._state = state + this._emitUpdates(state) +} diff --git a/app/scripts/lib/stream-utils.js b/app/scripts/lib/stream-utils.js index 12560ffd8..fd4417d94 100644 --- a/app/scripts/lib/stream-utils.js +++ b/app/scripts/lib/stream-utils.js @@ -1,9 +1,11 @@ const Through = require('through2') +const ObjectMultiplex = require('./obj-multiplex') module.exports = { jsonParseStream: jsonParseStream, jsonStringifyStream: jsonStringifyStream, + setupMultiplex: setupMultiplex, } function jsonParseStream(){ @@ -19,3 +21,17 @@ function jsonStringifyStream(){ cb() }) } + +function setupMultiplex(connectionStream){ + var mx = ObjectMultiplex() + connectionStream.pipe(mx).pipe(connectionStream) + mx.on('error', function(err) { + console.error(err) + // connectionStream.destroy() + }) + connectionStream.on('error', function(err) { + console.error(err) + mx.destroy() + }) + return mx +}
\ No newline at end of file diff --git a/app/scripts/popup.js b/app/scripts/popup.js index 3049ff2c3..85b3e30f9 100644 --- a/app/scripts/popup.js +++ b/app/scripts/popup.js @@ -1,7 +1,6 @@ const url = require('url') const EventEmitter = require('events').EventEmitter const async = require('async') -const ObjectMultiplex = require('./lib/obj-multiplex') const Dnode = require('dnode') const Web3 = require('web3') const MetaMaskUi = require('../../ui') @@ -9,6 +8,7 @@ const MetaMaskUiCss = require('../../ui/css') const injectCss = require('inject-css') const PortStream = require('./lib/port-stream.js') const StreamProvider = require('./lib/stream-provider.js') +const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex // setup app var css = MetaMaskUiCss() @@ -24,21 +24,13 @@ function connectToAccountManager(cb){ var pluginPort = chrome.runtime.connect({name: 'popup'}) var portStream = new PortStream(pluginPort) // setup multiplexing - var mx = ObjectMultiplex() - portStream.pipe(mx).pipe(portStream) - mx.on('error', function(err) { - console.error(err) - portStream.destroy() - }) - portStream.on('error', function(err) { - console.error(err) - mx.destroy() - }) - linkDnode(mx.createStream('dnode'), cb) - linkWeb3(mx.createStream('provider')) + var mx = setupMultiplex(portStream) + // connect features + setupControllerConnection(mx.createStream('controller'), cb) + setupWeb3Connection(mx.createStream('provider')) } -function linkWeb3(stream){ +function setupWeb3Connection(stream){ var remoteProvider = new StreamProvider() remoteProvider.pipe(stream).pipe(remoteProvider) stream.on('error', console.error.bind(console)) @@ -46,7 +38,7 @@ function linkWeb3(stream){ global.web3 = new Web3(remoteProvider) } -function linkDnode(stream, cb){ +function setupControllerConnection(stream, cb){ var eventEmitter = new EventEmitter() var background = Dnode({ sendUpdate: function(state){ |