From 11c8c07bfc6677e347873f02ae8c401f8d6c4dcf Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 22 Sep 2017 13:59:25 -0700 Subject: Refactor eth-store into account-tracker EthStore was only being used for tracking account balances and nonces now, so I removed its block-tracking duties, renamed it account-tracker, and removed it as a dependency from `KeyringController`, so that KRC can go live on without a hard dep on it. --- app/scripts/controllers/balances.js | 64 ------------------- app/scripts/controllers/computed-balances.js | 64 +++++++++++++++++++ app/scripts/controllers/transactions.js | 6 +- app/scripts/lib/account-tracker.js | 96 ++++++++++++++++++++++++++++ app/scripts/lib/eth-store.js | 96 ---------------------------- app/scripts/metamask-controller.js | 26 +++++--- 6 files changed, 179 insertions(+), 173 deletions(-) delete mode 100644 app/scripts/controllers/balances.js create mode 100644 app/scripts/controllers/computed-balances.js create mode 100644 app/scripts/lib/account-tracker.js delete mode 100644 app/scripts/lib/eth-store.js (limited to 'app') diff --git a/app/scripts/controllers/balances.js b/app/scripts/controllers/balances.js deleted file mode 100644 index 89c2ca95d..000000000 --- a/app/scripts/controllers/balances.js +++ /dev/null @@ -1,64 +0,0 @@ -const ObservableStore = require('obs-store') -const extend = require('xtend') -const BalanceController = require('./balance') - -class BalancesController { - - constructor (opts = {}) { - const { ethStore, txController } = opts - this.ethStore = ethStore - this.txController = txController - - const initState = extend({ - computedBalances: {}, - }, opts.initState) - this.store = new ObservableStore(initState) - this.balances = {} - - this._initBalanceUpdating() - } - - updateAllBalances () { - for (let address in this.balances) { - this.balances[address].updateBalance() - } - } - - _initBalanceUpdating () { - const store = this.ethStore.getState() - this.addAnyAccountsFromStore(store) - this.ethStore.subscribe(this.addAnyAccountsFromStore.bind(this)) - } - - addAnyAccountsFromStore(store) { - const balances = store.accounts - - for (let address in balances) { - this.trackAddressIfNotAlready(address) - } - } - - trackAddressIfNotAlready (address) { - const state = this.store.getState() - if (!(address in state.computedBalances)) { - this.trackAddress(address) - } - } - - trackAddress (address) { - let updater = new BalanceController({ - address, - ethStore: this.ethStore, - txController: this.txController, - }) - updater.store.subscribe((accountBalance) => { - let newState = this.store.getState() - newState.computedBalances[address] = accountBalance - this.store.updateState(newState) - }) - this.balances[address] = updater - updater.updateBalance() - } -} - -module.exports = BalancesController diff --git a/app/scripts/controllers/computed-balances.js b/app/scripts/controllers/computed-balances.js new file mode 100644 index 000000000..a85eb5590 --- /dev/null +++ b/app/scripts/controllers/computed-balances.js @@ -0,0 +1,64 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') +const BalanceController = require('./balance') + +class ComputedbalancesController { + + constructor (opts = {}) { + const { ethStore, txController } = opts + this.ethStore = ethStore + this.txController = txController + + const initState = extend({ + computedBalances: {}, + }, opts.initState) + this.store = new ObservableStore(initState) + this.balances = {} + + this._initBalanceUpdating() + } + + updateAllBalances () { + for (let address in this.balances) { + this.balances[address].updateBalance() + } + } + + _initBalanceUpdating () { + const store = this.ethStore.getState() + this.addAnyAccountsFromStore(store) + this.ethStore.subscribe(this.addAnyAccountsFromStore.bind(this)) + } + + addAnyAccountsFromStore(store) { + const balances = store.accounts + + for (let address in balances) { + this.trackAddressIfNotAlready(address) + } + } + + trackAddressIfNotAlready (address) { + const state = this.store.getState() + if (!(address in state.computedBalances)) { + this.trackAddress(address) + } + } + + trackAddress (address) { + let updater = new BalanceController({ + address, + ethStore: this.ethStore, + txController: this.txController, + }) + updater.store.subscribe((accountBalance) => { + let newState = this.store.getState() + newState.computedBalances[address] = accountBalance + this.store.updateState(newState) + }) + this.balances[address] = updater + updater.updateBalance() + } +} + +module.exports = ComputedbalancesController diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 59a3f5329..2aff4e5ff 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -22,7 +22,7 @@ module.exports = class TransactionController extends EventEmitter { this.provider = opts.provider this.blockTracker = opts.blockTracker this.signEthTx = opts.signTransaction - this.ethStore = opts.ethStore + this.accountTracker = opts.accountTracker this.nonceTracker = new NonceTracker({ provider: this.provider, @@ -52,7 +52,7 @@ module.exports = class TransactionController extends EventEmitter { provider: this.provider, nonceTracker: this.nonceTracker, getBalance: (address) => { - const account = this.ethStore.getState().accounts[address] + const account = this.accountTracker.getState().accounts[address] if (!account) return return account.balance }, @@ -73,7 +73,7 @@ module.exports = class TransactionController extends EventEmitter { this.blockTracker.on('rawBlock', this.pendingTxTracker.checkForTxInBlock.bind(this.pendingTxTracker)) // this is a little messy but until ethstore has been either // removed or redone this is to guard against the race condition - // where ethStore hasent been populated by the results yet + // where accountTracker hasent been populated by the results yet this.blockTracker.once('latest', () => { this.blockTracker.on('latest', this.pendingTxTracker.resubmitPendingTxs.bind(this.pendingTxTracker)) }) diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js new file mode 100644 index 000000000..bf949597b --- /dev/null +++ b/app/scripts/lib/account-tracker.js @@ -0,0 +1,96 @@ +/* Account Tracker + * + * This module is responsible for tracking any number of accounts + * and caching their current balances & transaction counts. + * + * It also tracks transaction hashes, and checks their inclusion status + * on each new block. + */ + +const async = require('async') +const EthQuery = require('eth-query') +const ObservableStore = require('obs-store') +function noop () {} + + +class EthereumStore extends ObservableStore { + + constructor (opts = {}) { + super({ + accounts: {}, + }) + this._provider = opts.provider + this._query = new EthQuery(this._provider) + this._blockTracker = opts.blockTracker + // subscribe to latest block + this._blockTracker.on('block', this._updateForBlock.bind(this)) + // blockTracker.currentBlock may be null + this._currentBlockNumber = this._blockTracker.currentBlock + } + + // + // public + // + + addAccount (address) { + const accounts = this.getState().accounts + accounts[address] = {} + this.updateState({ accounts }) + if (!this._currentBlockNumber) return + this._updateAccount(address) + } + + removeAccount (address) { + const accounts = this.getState().accounts + delete accounts[address] + this.updateState({ accounts }) + } + + // + // private + // + + _updateForBlock (block) { + const blockNumber = '0x' + block.number.toString('hex') + this._currentBlockNumber = blockNumber + + async.parallel([ + this._updateAccounts.bind(this), + ], (err) => { + if (err) return console.error(err) + this.emit('block', this.getState()) + }) + } + + _updateAccounts (cb = noop) { + const accounts = this.getState().accounts + const addresses = Object.keys(accounts) + async.each(addresses, this._updateAccount.bind(this), cb) + } + + _updateAccount (address, cb = noop) { + const accounts = this.getState().accounts + this._getAccount(address, (err, result) => { + if (err) return cb(err) + result.address = address + // only populate if the entry is still present + if (accounts[address]) { + accounts[address] = result + this.updateState({ accounts }) + } + cb(null, result) + }) + } + + _getAccount (address, cb = noop) { + const query = this._query + async.parallel({ + balance: query.getBalance.bind(query, address), + nonce: query.getTransactionCount.bind(query, address), + code: query.getCode.bind(query, address), + }, cb) + } + +} + +module.exports = EthereumStore diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js deleted file mode 100644 index ff22eca4a..000000000 --- a/app/scripts/lib/eth-store.js +++ /dev/null @@ -1,96 +0,0 @@ -/* Ethereum Store - * - * This module is responsible for tracking any number of accounts - * and caching their current balances & transaction counts. - * - * It also tracks transaction hashes, and checks their inclusion status - * on each new block. - */ - -const async = require('async') -const EthQuery = require('eth-query') -const ObservableStore = require('obs-store') -function noop () {} - - -class EthereumStore extends ObservableStore { - - constructor (opts = {}) { - super({ - accounts: {}, - }) - this._provider = opts.provider - this._query = new EthQuery(this._provider) - this._blockTracker = opts.blockTracker - // subscribe to latest block - this._blockTracker.on('block', this._updateForBlock.bind(this)) - // blockTracker.currentBlock may be null - this._currentBlockNumber = this._blockTracker.currentBlock - } - - // - // public - // - - addAccount (address) { - const accounts = this.getState().accounts - accounts[address] = {} - this.updateState({ accounts }) - if (!this._currentBlockNumber) return - this._updateAccount(address) - } - - removeAccount (address) { - const accounts = this.getState().accounts - delete accounts[address] - this.updateState({ accounts }) - } - - // - // private - // - - _updateForBlock (block) { - const blockNumber = '0x' + block.number.toString('hex') - this._currentBlockNumber = blockNumber - - async.parallel([ - this._updateAccounts.bind(this), - ], (err) => { - if (err) return console.error(err) - this.emit('block', this.getState()) - }) - } - - _updateAccounts (cb = noop) { - const accounts = this.getState().accounts - const addresses = Object.keys(accounts) - async.each(addresses, this._updateAccount.bind(this), cb) - } - - _updateAccount (address, cb = noop) { - const accounts = this.getState().accounts - this._getAccount(address, (err, result) => { - if (err) return cb(err) - result.address = address - // only populate if the entry is still present - if (accounts[address]) { - accounts[address] = result - this.updateState({ accounts }) - } - cb(null, result) - }) - } - - _getAccount (address, cb = noop) { - const query = this._query - async.parallel({ - balance: query.getBalance.bind(query, address), - nonce: query.getTransactionCount.bind(query, address), - code: query.getCode.bind(query, address), - }, cb) - } - -} - -module.exports = EthereumStore diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 02c06ead2..b1cfe1a2d 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -4,7 +4,7 @@ const promiseToCallback = require('promise-to-callback') const pipe = require('pump') const Dnode = require('dnode') const ObservableStore = require('obs-store') -const EthStore = require('./lib/eth-store') +const AccountTracker = require('./lib/account-tracker') const EthQuery = require('eth-query') const streamIntoProvider = require('web3-stream-provider/handler') const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex @@ -81,19 +81,25 @@ module.exports = class MetamaskController extends EventEmitter { // eth data query tools this.ethQuery = new EthQuery(this.provider) - this.ethStore = new EthStore({ - provider: this.provider, - blockTracker: this.provider, - }) // key mgmt this.keyringController = new KeyringController({ initState: initState.KeyringController, - ethStore: this.ethStore, + accountTracker: this.accountTracker, getNetwork: this.networkController.getNetworkState.bind(this.networkController), }) + + // account tracker watches balances, nonces, and any code at their address. + this.accountTracker = new AccountTracker({ + provider: this.provider, + blockTracker: this.provider, + }) this.keyringController.on('newAccount', (address) => { this.preferencesController.setSelectedAddress(address) + this.accountTracker.addAccount(address) + }) + this.keyringController.on('removedAccount', (address) => { + this.accountTracker.removeAccount(address) }) // address book controller @@ -112,13 +118,13 @@ module.exports = class MetamaskController extends EventEmitter { provider: this.provider, blockTracker: this.provider, ethQuery: this.ethQuery, - ethStore: this.ethStore, + accountTracker: this.accountTracker, }) this.txController.on('newUnaprovedTx', opts.showUnapprovedTx.bind(opts)) // computed balances (accounting for pending transactions) this.balancesController = new BalancesController({ - ethStore: this.ethStore, + accountTracker: this.accountTracker, txController: this.txController, }) this.networkController.on('networkDidChange', () => { @@ -177,7 +183,7 @@ module.exports = class MetamaskController extends EventEmitter { // manual mem state subscriptions this.networkController.store.subscribe(this.sendUpdate.bind(this)) - this.ethStore.subscribe(this.sendUpdate.bind(this)) + this.accountTracker.subscribe(this.sendUpdate.bind(this)) this.txController.memStore.subscribe(this.sendUpdate.bind(this)) this.balancesController.store.subscribe(this.sendUpdate.bind(this)) this.messageManager.memStore.subscribe(this.sendUpdate.bind(this)) @@ -260,7 +266,7 @@ module.exports = class MetamaskController extends EventEmitter { isInitialized, }, this.networkController.store.getState(), - this.ethStore.getState(), + this.accountTracker.getState(), this.txController.memStore.getState(), this.messageManager.memStore.getState(), this.personalMessageManager.memStore.getState(), -- cgit v1.2.3