Fix blocks tracking: reset recent blocks array on network change

This commit is contained in:
Victor Baranov 2019-07-17 19:20:36 +03:00
parent 5a1aff9315
commit 3e148a750c
6 changed files with 217 additions and 28 deletions

View File

@ -13,6 +13,8 @@ const createLocalhostClient = require('./createLocalhostClient')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
const ethNetProps = require('eth-net-props')
const parse = require('url-parse')
const extend = require('extend')
const networks = { networkList: {} }
const {
ROPSTEN,
@ -41,8 +43,21 @@ const env = process.env.METAMASK_ENV
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const testMode = (METAMASK_DEBUG || env === 'test')
let defaultProviderConfigType
if (process.env.IN_TEST === 'true') {
defaultProviderConfigType = LOCALHOST
} else if (testMode) {
defaultProviderConfigType = POA_SOKOL
} else {
defaultProviderConfigType = POA
}
const defaultProviderConfig = {
type: testMode ? POA_SOKOL : POA,
type: defaultProviderConfigType,
}
const defaultNetworkConfig = {
ticker: 'POA',
}
module.exports = class NetworkController extends EventEmitter {
@ -55,7 +70,8 @@ module.exports = class NetworkController extends EventEmitter {
// create stores
this.providerStore = new ObservableStore(providerConfig)
this.networkStore = new ObservableStore('loading')
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
this.networkConfig = new ObservableStore(defaultNetworkConfig)
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore, settings: this.networkConfig })
this.on('networkDidChange', this.lookupNetwork)
// provider and block tracker
this._provider = null
@ -67,8 +83,8 @@ module.exports = class NetworkController extends EventEmitter {
initializeProvider (providerParams) {
this._baseProviderParams = providerParams
const { type, rpcTarget } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget })
const { type, rpcTarget, chainId, ticker, nickname } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget, chainId, ticker, nickname })
this.lookupNetwork()
}
@ -88,7 +104,20 @@ module.exports = class NetworkController extends EventEmitter {
return this.networkStore.getState()
}
setNetworkState (network) {
getNetworkConfig () {
return this.networkConfig.getState()
}
setNetworkState (network, type) {
if (network === 'loading') {
return this.networkStore.putState(network)
}
// type must be defined
if (!type) {
return
}
network = networks.networkList[type] && networks.networkList[type].chainId ? networks.networkList[type].chainId : network
return this.networkStore.putState(network)
}
@ -97,33 +126,43 @@ module.exports = class NetworkController extends EventEmitter {
}
lookupNetwork () {
const { type, rpcTarget } = this.providerStore.getState()
// Prevent firing when provider is not defined.
if (!this._provider) {
return log.warn('NetworkController - lookupNetwork aborted due to missing provider')
}
const { type, rpcTarget } = this.providerStore.getState()
const ethQuery = new EthQuery(this._provider)
const initialNetwork = this.getNetworkState()
ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) return this.setNetworkState('loading')
const targetHost = parse(rpcTarget, true).host
const classicHost = parse(ethNetProps.RPCEndpoints(CLASSIC_CODE)[0], true).host
if (type === CLASSIC || targetHost === classicHost) {
network = CLASSIC_CODE.toString()
} // workaround to avoid Mainnet and Classic are having the same network ID
log.info('web3.getNetwork returned ' + network)
this.setNetworkState(network)
const currentNetwork = this.getNetworkState()
if (initialNetwork === currentNetwork) {
if (err) {
return this.setNetworkState('loading')
}
log.info('web3.getNetwork returned ' + network)
this.setNetworkState(network, type)
}
})
}
setRpcTarget (rpcTarget) {
setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '', rpcPrefs) {
const providerConfig = {
type: 'rpc',
rpcTarget,
chainId,
ticker,
nickname,
rpcPrefs,
}
this.providerConfig = providerConfig
}
async setProviderType (type) {
async setProviderType (type, rpcTarget = '', ticker = 'ETH', nickname = '') {
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
assert(INFURA_PROVIDER_TYPES.includes(type) ||
type === LOCALHOST ||
@ -135,7 +174,7 @@ module.exports = class NetworkController extends EventEmitter {
type === RSK ||
type === RSK_TESTNET
, `NetworkController - Unknown rpc type "${type}"`)
const providerConfig = { type }
const providerConfig = { type, rpcTarget, ticker, nickname }
this.providerConfig = providerConfig
}
@ -159,35 +198,35 @@ module.exports = class NetworkController extends EventEmitter {
_switchNetwork (opts) {
this.setNetworkState('loading')
this._configureProvider(opts)
this.emit('networkDidChange')
this.emit('networkDidChange', opts.type)
}
_configureProvider (opts) {
const { type, rpcTarget } = opts
const { type, rpcTarget, chainId, ticker, nickname } = opts
// infura type-based endpoints
const isInfura = INFURA_PROVIDER_TYPES.includes(type)
if (isInfura) {
this._configureInfuraProvider(opts)
// other type-based rpc endpoints
} else if (type === POA) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(POA_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(POA_CODE)[0], chainId, ticker, nickname })
} else if (type === DAI) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(DAI_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(DAI_CODE)[0], chainId, ticker, nickname })
} else if (type === POA_SOKOL) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(POA_SOKOL_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(POA_SOKOL_CODE)[0], chainId, ticker, nickname })
} else if (type === GOERLI_TESTNET) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(GOERLI_TESTNET_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(GOERLI_TESTNET_CODE)[0], chainId, ticker, nickname })
} else if (type === CLASSIC) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(CLASSIC_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(CLASSIC_CODE)[0], chainId, ticker, nickname })
} else if (type === RSK) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(RSK_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(RSK_CODE)[0], chainId, ticker, nickname })
} else if (type === RSK_TESTNET) {
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(RSK_TESTNET_CODE)[0] })
this._configureStandardProvider({ rpcUrl: ethNetProps.RPCEndpoints(RSK_TESTNET_CODE)[0], chainId, ticker, nickname })
} else if (type === LOCALHOST) {
this._configureLocalhostProvider()
// url-based rpc endpoints
} else if (type === 'rpc') {
this._configureStandardProvider({ rpcUrl: rpcTarget })
this._configureStandardProvider({ rpcUrl: rpcTarget, chainId, ticker, nickname })
} else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
}
@ -197,6 +236,11 @@ module.exports = class NetworkController extends EventEmitter {
log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient)
// setup networkConfig
var settings = {
ticker: 'ETH',
}
this.networkConfig.putState(settings)
}
_configureLocalhostProvider () {
@ -205,9 +249,22 @@ module.exports = class NetworkController extends EventEmitter {
this._setNetworkClient(networkClient)
}
_configureStandardProvider ({ rpcUrl }) {
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ rpcUrl })
// hack to add a 'rpc' network with chainId
networks.networkList['rpc'] = {
chainId: chainId,
rpcUrl,
ticker: ticker || 'ETH',
nickname,
}
// setup networkConfig
var settings = {
network: chainId,
}
settings = extend(settings, networks.networkList['rpc'])
this.networkConfig.putState(settings)
this._setNetworkClient(networkClient)
}

View File

@ -3,6 +3,8 @@ const extend = require('xtend')
const EthQuery = require('eth-query')
const log = require('loglevel')
const pify = require('pify')
const { isKnownProvider, ifRSKByProviderType } = require('../../../old-ui/app/util')
class RecentBlocksController {
@ -24,26 +26,58 @@ class RecentBlocksController {
*
*/
constructor (opts = {}) {
const { blockTracker, provider } = opts
const { blockTracker, provider, networkController } = opts
const { type } = networkController.getProviderConfig()
this.blockTracker = blockTracker
this.ethQuery = new EthQuery(provider)
this.historyLength = opts.historyLength || 40
this.setHistoryLength(type, opts)
const initState = extend({
recentBlocks: [],
}, opts.initState)
this.store = new ObservableStore(initState)
this.blockTracker.on('latest', async (newBlockNumberHex) => {
const blockListner = async (newBlockNumberHex) => {
try {
await this.processBlock(newBlockNumberHex)
} catch (err) {
log.error(err)
}
}
let isListening = false
if (!isKnownProvider(type) && type !== 'loading') {
this.blockTracker.on('latest', blockListner)
isListening = true
}
networkController.on('networkDidChange', (newType) => {
this.setHistoryLength(newType, opts)
this.resetState()
this.backfill()
if (isKnownProvider(newType) && isListening) {
this.blockTracker.removeListener('latest', blockListner)
} else if (
!isKnownProvider(type) &&
type !== 'loading' &&
!isListening
) {
this.blockTracker.on('latest', blockListner)
}
})
this.backfill()
}
/**
* Sets historyLength param
*
*/
setHistoryLength (type, opts) {
if (ifRSKByProviderType(type)) {
this.historyLength = 1
} else {
this.historyLength = opts.historyLength || 40
}
}
/**
* Sets store.recentBlocks to an empty array
*

View File

@ -130,6 +130,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.recentBlocksController = new RecentBlocksController({
blockTracker: this.blockTracker,
provider: this.provider,
networkController: this.networkController,
})
// account tracker watches balances, nonces, and any code at their address.

View File

@ -6,6 +6,18 @@ const {
DAI_CODE,
RSK_CODE,
RSK_TESTNET_CODE,
ROPSTEN,
RINKEBY,
KOVAN,
MAINNET,
LOCALHOST,
POA_SOKOL,
POA,
DAI,
GOERLI_TESTNET,
CLASSIC,
RSK,
RSK_TESTNET,
} = require('../../app/scripts/controllers/network/enums')
var valueTable = {
@ -54,9 +66,12 @@ module.exports = {
ifHardwareAcc,
getAllKeyRingsAccounts,
ifRSK,
ifRSKByProviderType,
ifPOA,
toChecksumAddress,
isValidChecksumAddress,
isInfuraProvider,
isKnownProvider,
}
function valuesFor (obj) {
@ -391,6 +406,11 @@ function ifRSK (network) {
return numericNet === RSK_CODE || numericNet === RSK_TESTNET_CODE
}
function ifRSKByProviderType (type) {
if (!type) return false
return type === RSK || type === RSK_TESTNET
}
function ifPOA (network) {
if (!network) return false
const numericNet = isNaN(network) ? network : parseInt(network)
@ -424,3 +444,21 @@ function toChecksumAddress (network, address, chainId = null) {
function isValidChecksumAddress (network, address) {
return isValidAddress(address, network) && toChecksumAddress(network, address) === address
}
function isInfuraProvider (type) {
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
return INFURA_PROVIDER_TYPES.includes(type)
}
function isKnownProvider (type) {
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
return INFURA_PROVIDER_TYPES.includes(type) ||
type === LOCALHOST ||
type === POA_SOKOL ||
type === POA ||
type === DAI ||
type === GOERLI_TESTNET ||
type === CLASSIC ||
type === RSK ||
type === RSK_TESTNET
}

View File

@ -47,7 +47,7 @@ describe('# Network Controller', function () {
describe('#setNetworkState', function () {
it('should update the network', function () {
networkController.setNetworkState(1)
networkController.setNetworkState(1, 'mainnet')
const networkState = networkController.getNetworkState()
assert.equal(networkState, 1, 'network is 1')
})

View File

@ -14,9 +14,12 @@ const {
normalizeNumberToWei,
isHex,
ifRSK,
ifRSKByProviderType,
ifPOA,
toChecksumAddress,
isValidChecksumAddress,
isInfuraProvider,
isKnownProvider,
} = require('../../../../old-ui/app/util')
const ethUtil = require('ethereumjs-util')
let ethInWei = '1'
@ -347,6 +350,18 @@ describe('normalizing values', function () {
})
})
describe('#ifRSKByProviderType', function () {
it('checks if this is RSK chain based on provider type', function () {
var result1 = ifRSKByProviderType('rsk')
assert(result1)
var result2 = ifRSKByProviderType('rsk_testnet')
assert(result2)
var result3 = ifRSKByProviderType('mainnet')
assert(!result3)
var result4 = ifRSKByProviderType()
assert(!result4)
})
})
describe('#ifPOA', function () {
it('checks if this is POA chain', function () {
@ -388,4 +403,48 @@ describe('normalizing values', function () {
assert(resultNotRSK)
})
})
describe('#isInfuraProvider', function () {
it('checks, that the given provider is Infura provider', function () {
var resultKovan = isInfuraProvider('kovan')
assert(resultKovan)
var resultRopsten = isInfuraProvider('ropsten')
assert(resultRopsten)
var resultRinkeby = isInfuraProvider('rinkeby')
assert(resultRinkeby)
var resultMainnet = isInfuraProvider('mainnet')
assert(resultMainnet)
var resultGoerli = !isInfuraProvider('goerli_testnet')
assert(resultGoerli)
var resultSokol = !isInfuraProvider('sokol')
assert(resultSokol)
var resultClassic = !isInfuraProvider('classic')
assert(resultClassic)
var resultRSK = !isInfuraProvider('rsk')
assert(resultRSK)
})
})
describe('#isKnownProvider', function () {
it('checks, that the given provider is Infura provider', function () {
var resultKovan = isKnownProvider('kovan')
assert(resultKovan)
var resultRopsten = isKnownProvider('ropsten')
assert(resultRopsten)
var resultRinkeby = isKnownProvider('rinkeby')
assert(resultRinkeby)
var resultMainnet = isKnownProvider('mainnet')
assert(resultMainnet)
var resultGoerli = isKnownProvider('goerli_testnet')
assert(resultGoerli)
var resultSokol = isKnownProvider('sokol')
assert(resultSokol)
var resultClassic = isKnownProvider('classic')
assert(resultClassic)
var resultRSK = isKnownProvider('rsk')
assert(resultRSK)
var resultUnknown = !isKnownProvider('unknown_network')
assert(resultUnknown)
})
})
})