forked from MetaMask/eth-json-rpc-middleware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblock-reemit.js
66 lines (59 loc) · 2.48 KB
/
block-reemit.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
const clone = require('clone')
const pify = require('pify')
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const blockTagParamIndex = require('./cache-utils').blockTagParamIndex
// `<nil>` comes from https://github.com/ethereum/go-ethereum/issues/16925
const emptyValues = [undefined, null, '\u003cnil\u003e']
//
// BlockReEmitMiddleware handles requests with "latest" block references by
// creating a new request with an explicit block number provided by a
// blockTracker. It will retry a request that returns with an empty response.
// Its useful for dealing with load-balanced ethereum JSON RPC
// nodes that are not always in sync with each other.
//
module.exports = createBlockReEmitMiddleware
function createBlockReEmitMiddleware (opts = {}) {
const { blockTracker, provider } = opts
if (!blockTracker) throw Error('BlockReEmitMiddleware - mandatory "blockTracker" option is missing.')
if (!provider) throw Error('BlockReEmitMiddleware - mandatory "provider" option is missing.')
return createAsyncMiddleware(async (req, res, next) => {
const blockRefIndex = blockTagParamIndex(req)
// skip if method does not include blockRef
if (blockRefIndex === undefined) return next()
// skip if not "latest"
let blockRef = req.params[blockRefIndex]
// omitted blockRef implies "latest"
if (blockRef === undefined) blockRef = 'latest'
if (blockRef !== 'latest') return next()
// lookup latest block
const latestBlockNumber = await blockTracker.getLatestBlock()
// re-emit request with specific block-ref
const childRequest = clone(req)
childRequest.params[blockRefIndex] = latestBlockNumber
// attempt child request until non-empty response is received
const childRes = await retry(10, async () => {
const childRes = await pify(provider.sendAsync).call(provider, childRequest)
// verify result
if (emptyValues.includes(childRes.result)) {
throw new Error('BlockReEmitMiddleware - empty response')
}
return childRes
})
// copy child response onto original response
res.result = childRes.result
res.error = childRes.error
})
}
async function retry(maxRetries, asyncFn) {
for (let index = 0; index < maxRetries; index++) {
try {
return await asyncFn()
} catch (err) {
await timeout(1000)
}
}
throw new Error('BlockReEmitMiddleware - retries exhausted')
}
function timeout(duration) {
return new Promise(resolve => setTimeout(resolve, duration))
}