aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorkumavis <kumavis@users.noreply.github.com>2016-04-16 04:06:28 +0800
committerkumavis <kumavis@users.noreply.github.com>2016-04-16 04:06:28 +0800
commit83b8741bbb335ad1629b72180616835dbb3f5433 (patch)
treeb8adfc80551c1032dc6fc43aa3a5055169446143 /app
parent81fc1e5f7dc958af40d8113b80c8cfb38c2016ef (diff)
parentd6114292d0c05b42b1d28d75baad8af0c06509a1 (diff)
downloadtangerine-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.js96
-rw-r--r--app/scripts/inpage.js80
-rw-r--r--app/scripts/lib/config-manager.js27
-rw-r--r--app/scripts/lib/obj-multiplex.js2
-rw-r--r--app/scripts/lib/remote-store.js97
-rw-r--r--app/scripts/lib/stream-utils.js16
-rw-r--r--app/scripts/popup.js22
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){