diff options
author | Frankie <frankie.diamond@gmail.com> | 2017-10-12 20:45:10 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-12 20:45:10 +0800 |
commit | c4adefcc530535c41a68cc915a20a05c7fa02791 (patch) | |
tree | a525a4fbad65a33bc3bc6cd9d530b22e51ea3e89 | |
parent | 6072e71a53d015423fde254fe08674a9226d3868 (diff) | |
parent | 5c5f9297f78c965b38087b960acfec472e16818b (diff) | |
download | tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.tar tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.tar.gz tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.tar.bz2 tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.tar.lz tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.tar.xz tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.tar.zst tangerine-wallet-browser-c4adefcc530535c41a68cc915a20a05c7fa02791.zip |
Merge pull request #2318 from MetaMask/network-controller-client
NetworkController refactor for new EthClient interface
-rw-r--r-- | app/scripts/controllers/network.js | 54 | ||||
-rw-r--r-- | app/scripts/controllers/transactions.js | 1 | ||||
-rw-r--r-- | app/scripts/lib/events-proxy.js | 10 | ||||
-rw-r--r-- | app/scripts/lib/nonce-tracker.js | 10 | ||||
-rw-r--r-- | app/scripts/lib/obj-proxy.js | 19 | ||||
-rw-r--r-- | app/scripts/metamask-controller.js | 68 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | test/integration/lib/first-time.js | 3 | ||||
-rw-r--r-- | test/unit/network-contoller-test.js | 25 | ||||
-rw-r--r-- | test/unit/nonce-tracker-test.js | 7 | ||||
-rw-r--r-- | ui/app/app.js | 2 |
11 files changed, 105 insertions, 96 deletions
diff --git a/app/scripts/controllers/network.js b/app/scripts/controllers/network.js index 0f9db4d53..64ed4b7c2 100644 --- a/app/scripts/controllers/network.js +++ b/app/scripts/controllers/network.js @@ -1,11 +1,12 @@ const assert = require('assert') const EventEmitter = require('events') -const createMetamaskProvider = require('web3-provider-engine/zero.js') const ObservableStore = require('obs-store') const ComposedStore = require('obs-store/lib/composed') const extend = require('xtend') const EthQuery = require('eth-query') +const createEthRpcClient = require('eth-rpc-client') const createEventEmitterProxy = require('../lib/events-proxy.js') +const createObjectProxy = require('../lib/obj-proxy.js') const RPC_ADDRESS_LIST = require('../config.js').network const DEFAULT_RPC = RPC_ADDRESS_LIST['rinkeby'] @@ -17,7 +18,8 @@ module.exports = class NetworkController extends EventEmitter { this.networkStore = new ObservableStore('loading') this.providerStore = new ObservableStore(config.provider) this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) - this._proxy = createEventEmitterProxy() + this.providerProxy = createObjectProxy() + this.blockTrackerProxy = createEventEmitterProxy() this.on('networkDidChange', this.lookupNetwork) } @@ -25,12 +27,11 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (_providerParams) { this._baseProviderParams = _providerParams const rpcUrl = this.getCurrentRpcAddress() - this._configureStandardProvider({ rpcUrl }) - this._proxy.on('block', this._logBlock.bind(this)) - this._proxy.on('error', this.verifyNetwork.bind(this)) - this.ethQuery = new EthQuery(this._proxy) + this._configureStandardClient({ rpcUrl }) + this.blockTrackerProxy.on('block', this._logBlock.bind(this)) + this.blockTrackerProxy.on('error', this.verifyNetwork.bind(this)) + this.ethQuery = new EthQuery(this.providerProxy) this.lookupNetwork() - return this._proxy } verifyNetwork () { @@ -76,8 +77,10 @@ module.exports = class NetworkController extends EventEmitter { assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`) // skip if type already matches if (type === this.getProviderConfig().type) return + // lookup rpcTarget for typecreateMetamaskProvider const rpcTarget = this.getRpcAddressForType(type) assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`) + // update connectioncreateMetamaskProvider this.providerStore.updateState({ type, rpcTarget }) this._switchNetwork({ rpcUrl: rpcTarget }) } @@ -97,32 +100,29 @@ module.exports = class NetworkController extends EventEmitter { _switchNetwork (providerParams) { this.setNetworkState('loading') - this._configureStandardProvider(providerParams) + this._configureStandardClient(providerParams) this.emit('networkDidChange') } - _configureStandardProvider(_providerParams) { + _configureStandardClient(_providerParams) { const providerParams = extend(this._baseProviderParams, _providerParams) - const provider = createMetamaskProvider(providerParams) - this._setProvider(provider) - } - - _setProvider (provider) { - // collect old block tracker events - const oldProvider = this._provider - let blockTrackerHandlers - if (oldProvider) { - // capture old block handlers - blockTrackerHandlers = oldProvider._blockTracker.proxyEventHandlers - // tear down - oldProvider.removeAllListeners() - oldProvider.stop() + const client = createEthRpcClient(providerParams) + this._setClient(client) + } + + _setClient (newClient) { + // teardown old client + const oldClient = this._currentClient + if (oldClient) { + oldClient.blockTracker.stop() + // asyncEventEmitter lacks a "removeAllListeners" method + // oldClient.blockTracker.removeAllListeners + oldClient.blockTracker._events = {} } - // override block tracler - provider._blockTracker = createEventEmitterProxy(provider._blockTracker, blockTrackerHandlers) // set as new provider - this._provider = provider - this._proxy.setTarget(provider) + this._currentClient = newClient + this.providerProxy.setTarget(newClient.provider) + this.blockTrackerProxy.setTarget(newClient.blockTracker) } _logBlock (block) { diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index ef659a300..d46dee230 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -46,6 +46,7 @@ module.exports = class TransactionController extends EventEmitter { this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.nonceTracker = new NonceTracker({ provider: this.provider, + blockTracker: this.blockTracker, getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager), getConfirmedTransactions: (address) => { return this.txStateManager.getFilteredTxList({ diff --git a/app/scripts/lib/events-proxy.js b/app/scripts/lib/events-proxy.js index d1199a278..840b06b1a 100644 --- a/app/scripts/lib/events-proxy.js +++ b/app/scripts/lib/events-proxy.js @@ -1,6 +1,5 @@ -module.exports = function createEventEmitterProxy(eventEmitter, listeners) { +module.exports = function createEventEmitterProxy(eventEmitter, eventHandlers = {}) { let target = eventEmitter - const eventHandlers = listeners || {} const proxy = new Proxy({}, { get: (obj, name) => { // intercept listeners @@ -14,9 +13,12 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { return true }, }) + proxy.setTarget(eventEmitter) + return proxy + function setTarget (eventEmitter) { target = eventEmitter - // migrate listeners + // migrate eventHandlers Object.keys(eventHandlers).forEach((name) => { eventHandlers[name].forEach((handler) => target.on(name, handler)) }) @@ -26,6 +28,4 @@ module.exports = function createEventEmitterProxy(eventEmitter, listeners) { eventHandlers[name].push(handler) target.on(name, handler) } - if (listeners) proxy.setTarget(eventEmitter) - return proxy }
\ No newline at end of file diff --git a/app/scripts/lib/nonce-tracker.js b/app/scripts/lib/nonce-tracker.js index 0029ac953..2af40a27f 100644 --- a/app/scripts/lib/nonce-tracker.js +++ b/app/scripts/lib/nonce-tracker.js @@ -4,8 +4,9 @@ const Mutex = require('await-semaphore').Mutex class NonceTracker { - constructor ({ provider, getPendingTransactions, getConfirmedTransactions }) { + constructor ({ provider, blockTracker, getPendingTransactions, getConfirmedTransactions }) { this.provider = provider + this.blockTracker = blockTracker this.ethQuery = new EthQuery(provider) this.getPendingTransactions = getPendingTransactions this.getConfirmedTransactions = getConfirmedTransactions @@ -53,7 +54,7 @@ class NonceTracker { } async _getCurrentBlock () { - const blockTracker = this._getBlockTracker() + const blockTracker = this.blockTracker const currentBlock = blockTracker.getCurrentBlock() if (currentBlock) return currentBlock return await Promise((reject, resolve) => { @@ -139,11 +140,6 @@ class NonceTracker { return { name: 'local', nonce: highest, details: { startPoint, highest } } } - // this is a hotfix for the fact that the blockTracker will - // change when the network changes - _getBlockTracker () { - return this.provider._blockTracker - } } module.exports = NonceTracker diff --git a/app/scripts/lib/obj-proxy.js b/app/scripts/lib/obj-proxy.js new file mode 100644 index 000000000..29ca1269f --- /dev/null +++ b/app/scripts/lib/obj-proxy.js @@ -0,0 +1,19 @@ +module.exports = function createObjectProxy(obj) { + let target = obj + const proxy = new Proxy({}, { + get: (obj, name) => { + // intercept setTarget + if (name === 'setTarget') return setTarget + return target[name] + }, + set: (obj, name, value) => { + target[name] = value + return true + }, + }) + return proxy + + function setTarget (obj) { + target = obj + } +}
\ No newline at end of file diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 727f48f1c..a742f3cba 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -81,9 +81,24 @@ module.exports = class MetamaskController extends EventEmitter { }) this.blacklistController.scheduleUpdates() - // rpc provider - this.provider = this.initializeProvider() - this.blockTracker = this.provider._blockTracker + // rpc provider and block tracker + this.networkController.initializeProvider({ + scaffold: { + eth_syncing: false, + web3_clientVersion: `MetaMask/v${version}`, + }, + // account mgmt + getAccounts: nodeify(this.getAccounts, this), + // tx signing + processTransaction: nodeify(this.newTransaction, this), + // old style msg signing + processMessage: this.newUnsignedMessage.bind(this), + // personal_sign msg signing + processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), + processTypedMessage: this.newUnsignedTypedMessage.bind(this), + }) + this.provider = this.networkController.providerProxy + this.blockTracker = this.networkController.blockTrackerProxy // eth data query tools this.ethQuery = new EthQuery(this.provider) @@ -218,36 +233,6 @@ module.exports = class MetamaskController extends EventEmitter { // Constructor helpers // - initializeProvider () { - const providerOpts = { - static: { - eth_syncing: false, - web3_clientVersion: `MetaMask/v${version}`, - }, - // account mgmt - getAccounts: (cb) => { - const isUnlocked = this.keyringController.memStore.getState().isUnlocked - const result = [] - const selectedAddress = this.preferencesController.getSelectedAddress() - - // only show address if account is unlocked - if (isUnlocked && selectedAddress) { - result.push(selectedAddress) - } - cb(null, result) - }, - // tx signing - processTransaction: nodeify(async (txParams) => await this.txController.newUnapprovedTransaction(txParams), this), - // old style msg signing - processMessage: this.newUnsignedMessage.bind(this), - // personal_sign msg signing - processPersonalMessage: this.newUnsignedPersonalMessage.bind(this), - processTypedMessage: this.newUnsignedTypedMessage.bind(this), - } - const providerProxy = this.networkController.initializeProvider(providerOpts) - return providerProxy - } - initPublicConfigStore () { // get init state const publicConfigStore = new ObservableStore() @@ -483,6 +468,18 @@ module.exports = class MetamaskController extends EventEmitter { // Opinionated Keyring Management // + async getAccounts () { + const isUnlocked = this.keyringController.memStore.getState().isUnlocked + const result = [] + const selectedAddress = this.preferencesController.getSelectedAddress() + + // only show address if account is unlocked + if (isUnlocked && selectedAddress) { + result.push(selectedAddress) + } + return result + } + addNewAccount (cb) { const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0] if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found')) @@ -529,6 +526,11 @@ module.exports = class MetamaskController extends EventEmitter { // Identity Management // + // this function wrappper lets us pass the fn reference before txController is instantiated + async newTransaction (txParams) { + return await this.txController.newUnapprovedTransaction(txParams) + } + newUnsignedMessage (msgParams, cb) { const msgId = this.messageManager.addUnapprovedMessage(msgParams) this.sendUpdate() diff --git a/package.json b/package.json index 225742487..2b7b2056a 100644 --- a/package.json +++ b/package.json @@ -71,9 +71,11 @@ "eth-contract-metadata": "^1.1.4", "eth-hd-keyring": "^1.2.1", "eth-json-rpc-filters": "^1.2.2", + "eth-json-rpc-middleware": "^1.4.3", "eth-keyring-controller": "^2.1.0", "eth-phishing-detect": "^1.1.4", "eth-query": "^2.1.2", + "eth-rpc-client": "^1.1.3", "eth-sig-util": "^1.4.0", "eth-simple-keyring": "^1.1.1", "eth-token-tracker": "^1.1.4", diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js index cedb14f6e..ee49d0901 100644 --- a/test/integration/lib/first-time.js +++ b/test/integration/lib/first-time.js @@ -3,6 +3,9 @@ const PASSWORD = 'password123' QUnit.module('first time usage') QUnit.test('render init screen', (assert) => { + // intercept reload attempts + window.onbeforeunload = () => true + const done = assert.async() runFirstTimeUsageTest(assert).then(done).catch((err) => { assert.notOk(err, `Error was thrown: ${err.stack}`) diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js index 0b3b5adeb..42ca40c56 100644 --- a/test/unit/network-contoller-test.js +++ b/test/unit/network-contoller-test.js @@ -14,15 +14,15 @@ describe('# Network Controller', function () { }, }) - networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) + networkController.initializeProvider(networkControllerProviderInit) }) describe('network', function () { describe('#provider', function () { it('provider should be updatable without reassignment', function () { - networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor) - const proxy = networkController._proxy - proxy.setTarget({ test: true, on: () => {} }) - assert.ok(proxy.test) + networkController.initializeProvider(networkControllerProviderInit) + const providerProxy = networkController.providerProxy + providerProxy.setTarget({ test: true }) + assert.ok(providerProxy.test) }) }) describe('#getNetworkState', function () { @@ -66,19 +66,4 @@ describe('# Network Controller', function () { }) }) -function dummyProviderConstructor() { - return { - // provider - sendAsync: noop, - // block tracker - _blockTracker: {}, - start: noop, - stop: noop, - on: noop, - addListener: noop, - once: noop, - removeAllListeners: noop, - } -} - function noop() {}
\ No newline at end of file diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js index 8970cf84d..77af2a21c 100644 --- a/test/unit/nonce-tracker-test.js +++ b/test/unit/nonce-tracker-test.js @@ -190,12 +190,13 @@ function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') { providerResultStub.result = providerStub const provider = { sendAsync: (_, cb) => { cb(undefined, providerResultStub) }, - _blockTracker: { - getCurrentBlock: () => '0x11b568', - }, + } + const blockTracker = { + getCurrentBlock: () => '0x11b568', } return new NonceTracker({ provider, + blockTracker, getPendingTransactions, getConfirmedTransactions, }) diff --git a/ui/app/app.js b/ui/app/app.js index 613577913..30d3766ab 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -319,7 +319,7 @@ App.prototype.renderNetworkDropdown = function () { [ h('i.fa.fa-question-circle.fa-lg.menu-icon'), 'Localhost 8545', - activeNetwork === 'http://localhost:8545' ? h('.check', '✓') : null, + providerType === 'localhost' ? h('.check', '✓') : null, ] ), |