|
1 | 1 | const fetch = require('node-fetch')
|
2 | 2 | const promiseRetry = require('promise-retry')
|
| 3 | +const { utils } = require('web3') |
3 | 4 | const { FALLBACK_RPC_URL_SWITCH_TIMEOUT } = require('../utils/constants')
|
4 | 5 |
|
5 | 6 | const { onInjected } = require('./injectedLogger')
|
@@ -39,19 +40,54 @@ function HttpListProvider(urls, options = {}) {
|
39 | 40 | this.options = { ...defaultOptions, ...options }
|
40 | 41 | this.currentIndex = 0
|
41 | 42 | this.lastTimeUsedPrimary = 0
|
| 43 | + this.latestBlock = 0 |
| 44 | + this.syncStateCheckerIntervalId = 0 |
42 | 45 |
|
43 | 46 | onInjected(logger => {
|
44 | 47 | this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` })
|
45 | 48 | })
|
46 | 49 | }
|
47 | 50 |
|
48 |
| -HttpListProvider.prototype.switchToFallbackRPC = function() { |
49 |
| - if (this.urls.length < 2) { |
50 |
| - return |
| 51 | +HttpListProvider.prototype.startSyncStateChecker = function(syncCheckInterval) { |
| 52 | + if (this.urls.length > 1 && syncCheckInterval > 0 && this.syncStateCheckerIntervalId === 0) { |
| 53 | + this.syncStateCheckerIntervalId = setInterval(this.checkLatestBlock.bind(this), syncCheckInterval) |
51 | 54 | }
|
| 55 | +} |
52 | 56 |
|
| 57 | +HttpListProvider.prototype.checkLatestBlock = function() { |
| 58 | + const payload = { jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] } |
| 59 | + this.send(payload, (error, result) => { |
| 60 | + if (error) { |
| 61 | + this.logger.warn({ oldBlock: this.latestBlock }, 'Failed to request latest block from all RPC urls') |
| 62 | + } else if (result.error) { |
| 63 | + this.logger.warn( |
| 64 | + { oldBlock: this.latestBlock, error: result.error.message }, |
| 65 | + 'Failed to make eth_blockNumber request due to unknown error, switching to fallback RPC' |
| 66 | + ) |
| 67 | + this.switchToFallbackRPC() |
| 68 | + } else { |
| 69 | + const blockNumber = utils.hexToNumber(result.result) |
| 70 | + if (blockNumber > this.latestBlock) { |
| 71 | + this.logger.debug({ oldBlock: this.latestBlock, newBlock: blockNumber }, 'Updating latest block number') |
| 72 | + this.latestBlock = blockNumber |
| 73 | + } else { |
| 74 | + this.logger.warn( |
| 75 | + { oldBlock: this.latestBlock, newBlock: blockNumber }, |
| 76 | + 'Latest block on the node was not updated since last request, switching to fallback RPC' |
| 77 | + ) |
| 78 | + this.switchToFallbackRPC() |
| 79 | + } |
| 80 | + } |
| 81 | + }) |
| 82 | +} |
| 83 | + |
| 84 | +HttpListProvider.prototype.switchToFallbackRPC = function(index) { |
53 | 85 | const prevIndex = this.currentIndex
|
54 |
| - const newIndex = (prevIndex + 1) % this.urls.length |
| 86 | + const newIndex = index || (prevIndex + 1) % this.urls.length |
| 87 | + if (this.urls.length < 2 || prevIndex === newIndex) { |
| 88 | + return |
| 89 | + } |
| 90 | + |
55 | 91 | this.logger.info(
|
56 | 92 | { index: newIndex, oldURL: this.urls[prevIndex], newURL: this.urls[newIndex] },
|
57 | 93 | 'Switching to fallback JSON-RPC URL'
|
@@ -80,11 +116,7 @@ HttpListProvider.prototype.send = async function send(payload, callback) {
|
80 | 116 |
|
81 | 117 | // if some of URLs failed to respond, current URL index is updated to the first URL that responded
|
82 | 118 | if (currentIndex !== index) {
|
83 |
| - this.logger.info( |
84 |
| - { index, oldURL: this.urls[currentIndex], newURL: this.urls[index] }, |
85 |
| - 'Switching to fallback JSON-RPC URL' |
86 |
| - ) |
87 |
| - this.currentIndex = index |
| 119 | + this.switchToFallbackRPC(index) |
88 | 120 | }
|
89 | 121 | callback(null, result)
|
90 | 122 | } catch (e) {
|
|
0 commit comments