network - create provider and block-tracker via json-rpc-engine

feature/default_network_editable
kumavis 7 years ago
parent 708422432c
commit 088d7930e0
  1. 34
      app/scripts/controllers/network/createInfuraClient.js
  2. 35
      app/scripts/controllers/network/createJsonRpcClient.js
  3. 33
      app/scripts/controllers/network/createLocalhostClient.js
  4. 43
      app/scripts/controllers/network/createMetamaskMiddleware.js
  5. 118
      app/scripts/controllers/network/network.js
  6. 1
      app/scripts/controllers/transactions/index.js
  7. 18
      app/scripts/controllers/transactions/nonce-tracker.js
  8. 7
      app/scripts/metamask-controller.js
  9. 4
      package.json
  10. 6
      test/unit/nonce-tracker-test.js

@ -0,0 +1,34 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-ref')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-cache')
const BlockTracker = require('eth-block-tracker')
module.exports = createInfuraClient
function createInfuraClient({ network }) {
const infuraMiddleware = createInfuraMiddleware({ network })
const blockProvider = providerFromMiddleware(infuraMiddleware)
const blockTracker = new BlockTracker({ provider: blockProvider })
const networkMiddleware = mergeMiddleware([
createBlockRefMiddleware({ blockTracker }),
createBlockCacheMiddleware({ blockTracker }),
createInflightMiddleware(),
createBlockTrackerInspectorMiddleware({ blockTracker }),
infuraMiddleware,
])
return { networkMiddleware, blockTracker }
}
// inspect if response contains a block ref higher than our latest block
const futureBlockRefRequests = ['eth_getTransactionByHash', 'eth_getTransactionReceipt']
function createBlockTrackerInspectorMiddleware ({ blockTracker }) {
return createAsyncMiddleware(async (req, res, next) => {
if (!futureBlockRefRequests.includes(req.method)) return next()
await next()
const blockNumber = Number.parseInt(res.result.blockNumber, 16)
const currentBlockNumber = Number.parseInt(blockTracker.getCurrentBlock(), 16)
if (blockNumber > currentBlockNumber) await blockTracker.checkForLatestBlock()
})
}

@ -0,0 +1,35 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-ref')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-cache')
const BlockTracker = require('eth-block-tracker')
module.exports = createJsonRpcClient
function createJsonRpcClient({ rpcUrl }) {
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
const blockProvider = providerFromMiddleware(fetchMiddleware)
const blockTracker = new BlockTracker({ provider: blockProvider })
const networkMiddleware = mergeMiddleware([
createBlockRefMiddleware({ blockTracker }),
createBlockCacheMiddleware({ blockTracker }),
createInflightMiddleware(),
createBlockTrackerInspectorMiddleware({ blockTracker }),
fetchMiddleware,
])
return { networkMiddleware, blockTracker }
}
// inspect if response contains a block ref higher than our latest block
const futureBlockRefRequests = ['eth_getTransactionByHash', 'eth_getTransactionReceipt']
function createBlockTrackerInspectorMiddleware ({ blockTracker }) {
return createAsyncMiddleware(async (req, res, next) => {
if (!futureBlockRefRequests.includes(req.method)) return next()
await next()
const blockNumber = Number.parseInt(res.result.blockNumber, 16)
const currentBlockNumber = Number.parseInt(blockTracker.getCurrentBlock(), 16)
if (blockNumber > currentBlockNumber) await blockTracker.checkForLatestBlock()
})
}

@ -0,0 +1,33 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-ref')
const createBlockRefMiddleware = require('eth-json-rpc-middleware/block-cache')
const BlockTracker = require('eth-block-tracker')
module.exports = createLocalhostClient
function createLocalhostClient() {
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
const blockProvider = providerFromMiddleware(fetchMiddleware)
const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
const networkMiddleware = mergeMiddleware([
createBlockRefMiddleware({ blockTracker }),
createBlockTrackerInspectorMiddleware({ blockTracker }),
fetchMiddleware,
])
return { networkMiddleware, blockTracker }
}
// inspect if response contains a block ref higher than our latest block
const futureBlockRefRequests = ['eth_getTransactionByHash', 'eth_getTransactionReceipt']
function createBlockTrackerInspectorMiddleware ({ blockTracker }) {
return createAsyncMiddleware(async (req, res, next) => {
if (!futureBlockRefRequests.includes(req.method)) return next()
await next()
const blockNumber = Number.parseInt(res.result.blockNumber, 16)
const currentBlockNumber = Number.parseInt(blockTracker.getCurrentBlock(), 16)
if (blockNumber > currentBlockNumber) await blockTracker.checkForLatestBlock()
})
}

@ -0,0 +1,43 @@
const mergeMiddleware = require('json-rpc-engine/src/mergeMiddleware')
const createScaffoldMiddleware = require('json-rpc-engine/src/scaffold')
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const createWalletSubprovider = require('eth-json-rpc-middleware/wallet')
module.exports = createMetamaskMiddleware
function createMetamaskMiddleware({
version,
getAccounts,
processTransaction,
processEthSignMessage,
processTypedMessage,
processPersonalMessage,
getPendingNonce
}) {
const metamaskMiddleware = mergeMiddleware([
createScaffoldMiddleware({
// staticSubprovider
eth_syncing: false,
web3_clientVersion: `MetaMask/v${version}`,
}),
createWalletSubprovider({
getAccounts,
processTransaction,
processEthSignMessage,
processTypedMessage,
processPersonalMessage,
}),
createPendingNonceMiddleware({ getPendingNonce }),
})
return metamaskMiddleware
}
function createPendingNonceMiddleware ({ getPendingNonce }) {
return createAsyncMiddleware(async (req, res, next) => {
if (req.method !== 'eth_getTransactionCount') return next()
const address = req.params[0]
const blockRef = req.params[1]
if (blockRef !== 'pending') return next()
req.result = await getPendingNonce(address)
})
}

@ -1,14 +1,17 @@
const assert = require('assert')
const EventEmitter = require('events')
const createMetamaskProvider = require('web3-provider-engine/zero.js')
const SubproviderFromProvider = require('web3-provider-engine/subproviders/provider.js')
const createInfuraProvider = require('eth-json-rpc-infura/src/createProvider')
const ObservableStore = require('obs-store')
const ComposedStore = require('obs-store/lib/composed')
const extend = require('xtend')
const EthQuery = require('eth-query')
const createEventEmitterProxy = require('../../lib/events-proxy.js')
const log = require('loglevel')
const createMetamaskMiddleware = require('./createMetamaskMiddleware')
const createInfuraClient = require('./createInfuraClient')
const createJsonRpcClient = require('./createJsonRpcClient')
const createLocalhostClient = require('./createLocalhostClient')
// const createEventEmitterProxy = require('../../lib/events-proxy.js')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
const {
ROPSTEN,
RINKEBY,
@ -38,21 +41,27 @@ module.exports = class NetworkController extends EventEmitter {
this.providerStore = new ObservableStore(providerConfig)
this.networkStore = new ObservableStore('loading')
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
// create event emitter proxy
this._proxy = createEventEmitterProxy()
this.on('networkDidChange', this.lookupNetwork)
// provider and block tracker
this._provider = null
this._blockTracker = null
// provider and block tracker proxies - because the network changes
this._providerProxy = null
this._blockTrackerProxy = null
}
initializeProvider (_providerParams) {
this._baseProviderParams = _providerParams
initializeProvider (providerParams) {
this._baseProviderParams = providerParams
const { type, rpcTarget } = this.providerStore.getState()
this._configureProvider({ type, rpcTarget })
this._proxy.on('block', this._logBlock.bind(this))
this._proxy.on('error', this.verifyNetwork.bind(this))
this.ethQuery = new EthQuery(this._proxy)
this.lookupNetwork()
return this._proxy
}
// return the proxies so the references will always be good
getProviderAndBlockTracker() {
const provider = this._providerProxy
const blockTracker = this._blockTracker
return { provider, blockTracker }
}
verifyNetwork () {
@ -74,10 +83,11 @@ module.exports = class NetworkController extends EventEmitter {
lookupNetwork () {
// Prevent firing when provider is not defined.
if (!this.ethQuery || !this.ethQuery.sendAsync) {
return log.warn('NetworkController - lookupNetwork aborted due to missing ethQuery')
if (!this._provider) {
return log.warn('NetworkController - lookupNetwork aborted due to missing provider')
}
this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
const ethQuery = new EthQuery(this._provider)
ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
if (err) return this.setNetworkState('loading')
log.info('web3.getNetwork returned ' + network)
this.setNetworkState(network)
@ -123,7 +133,7 @@ module.exports = class NetworkController extends EventEmitter {
this._configureInfuraProvider(opts)
// other type-based rpc endpoints
} else if (type === LOCALHOST) {
this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
this._configureLocalhostProvider()
// url-based rpc endpoints
} else if (type === 'rpc'){
this._configureStandardProvider({ rpcUrl: rpcTarget })
@ -133,47 +143,47 @@ module.exports = class NetworkController extends EventEmitter {
}
_configureInfuraProvider ({ type }) {
log.info('_configureInfuraProvider', type)
const infuraProvider = createInfuraProvider({ network: type })
const infuraSubprovider = new SubproviderFromProvider(infuraProvider)
const providerParams = extend(this._baseProviderParams, {
engineParams: {
pollingInterval: 8000,
blockTrackerProvider: infuraProvider,
},
dataSubprovider: infuraSubprovider,
})
const provider = createMetamaskProvider(providerParams)
this._setProvider(provider)
log.info('NetworkController - configureInfuraProvider', type)
const networkClient = createInfuraClient({ network: type })
this._setNetworkClient(networkClient)
}
_configureLocalhostProvider () {
log.info('NetworkController - configureLocalhostProvider')
const networkClient = createLocalhostClient()
this._setNetworkClient(networkClient)
}
_configureStandardProvider ({ rpcUrl }) {
const providerParams = extend(this._baseProviderParams, {
rpcUrl,
engineParams: {
pollingInterval: 8000,
},
})
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()
}
// override block tracler
provider._blockTracker = createEventEmitterProxy(provider._blockTracker, blockTrackerHandlers)
// set as new provider
log.info('NetworkController - configureStandardProvider', rpcUrl)
const networkClient = createJsonRpcClient({ rpcUrl })
this._setNetworkClient(networkClient)
}
_setNetworkClient ({ networkMiddleware, blockTracker }) {
const metamaskMiddleware = createMetamaskMiddleware(this._baseProviderParams)
const engine = new JsonRpcEngine()
engine.push(metamaskMiddleware)
engine.push(networkMiddleware)
const provider = providerFromEngine(engine)
this._setProviderAndBlockTracker({ provider, blockTracker })
}
_setProviderAndBlockTracker ({ provider, blockTracker }) {
// update or intialize proxies
if (this._providerProxy) {
this._providerProxy.setTarget(provider)
} else {
this._providerProxy = createSwappableProxy(provider)
}
if (this._blockTrackerProxy) {
this._blockTrackerProxy.setTarget(blockTracker)
} else {
this._blockTrackerProxy = createEventEmitterProxy(blockTracker)
}
// set new provider and blockTracker
this._provider = provider
this._proxy.setTarget(provider)
this._blockTracker = blockTracker
}
_logBlock (block) {

@ -63,6 +63,7 @@ class TransactionController extends EventEmitter {
this.store = this.txStateManager.store
this.nonceTracker = new NonceTracker({
provider: this.provider,
blockTracker: this.blockTracker,
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
getConfirmedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
})

@ -12,8 +12,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
@ -75,11 +76,10 @@ class NonceTracker {
}
async _getCurrentBlock () {
const blockTracker = this._getBlockTracker()
const currentBlock = blockTracker.getCurrentBlock()
const currentBlock = this.blockTracker.getCurrentBlock()
if (currentBlock) return currentBlock
return await new Promise((reject, resolve) => {
blockTracker.once('latest', resolve)
this.blockTracker.once('latest', resolve)
})
}
@ -171,16 +171,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
/**
@returns {Object} the current blockTracker
*/
_getBlockTracker () {
return this.provider._blockTracker
}
}
module.exports = NonceTracker

@ -103,8 +103,9 @@ module.exports = class MetamaskController extends EventEmitter {
this.blacklistController.scheduleUpdates()
// rpc provider
this.provider = this.initializeProvider()
this.blockTracker = this.provider._blockTracker
this.initializeProvider()
this.provider = this.networkController.getProviderAndBlockTracker().provider
this.blockTracker = this.networkController.getProviderAndBlockTracker().blockTracker
// token exchange rate tracker
this.tokenRatesController = new TokenRatesController({
@ -1033,7 +1034,7 @@ module.exports = class MetamaskController extends EventEmitter {
// create filter polyfill middleware
const filterMiddleware = createFilterMiddleware({
provider: this.provider,
blockTracker: this.provider._blockTracker,
blockTracker: this.blockTracker,
})
engine.push(createOriginMiddleware({ origin }))

@ -91,6 +91,7 @@
"ensnare": "^1.0.0",
"eslint-plugin-react": "^7.4.0",
"eth-bin-to-ops": "^1.0.1",
"eth-block-tracker": "github:metamask/eth-block-tracker#acbcfda348c309ece0fb96fad78d4861d08f5a91",
"eth-contract-metadata": "^1.1.5",
"eth-hd-keyring": "^1.2.1",
"eth-json-rpc-filters": "^1.2.6",
@ -129,7 +130,7 @@
"iframe-stream": "^3.0.0",
"inject-css": "^0.1.1",
"jazzicon": "^1.2.0",
"json-rpc-engine": "^3.6.1",
"json-rpc-engine": "^3.7.0",
"json-rpc-middleware-stream": "^1.0.1",
"lodash.debounce": "^4.0.8",
"lodash.memoize": "^4.1.2",
@ -185,6 +186,7 @@
"shallow-copy": "0.0.1",
"sw-controller": "^1.0.3",
"sw-stream": "^2.0.2",
"swappable-obj-proxy": "^1.0.0",
"textarea-caret": "^3.0.1",
"through2": "^2.0.3",
"valid-url": "^1.0.9",

@ -226,14 +226,14 @@ function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') {
providerResultStub.result = providerStub
const provider = {
sendAsync: (_, cb) => { cb(undefined, providerResultStub) },
_blockTracker: {
}
const blockTracker = {
getCurrentBlock: () => '0x11b568',
},
}
return new NonceTracker({
provider,
blockTracker,
getPendingTransactions,
getConfirmedTransactions,
})
}

Loading…
Cancel
Save