Skip to content

Commit ff9f3fb

Browse files
authored
Merge the develop branch to the master branch, preparation to v3.7.0
This merge contains the following set of changes: * [Oracle, Improvement] Periodic check for RPC sync state (#656)
2 parents bcf1614 + 297cb67 commit ff9f3fb

File tree

10 files changed

+80
-59
lines changed

10 files changed

+80
-59
lines changed

CONFIGURATION.md

+2
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ ORACLE_JSONRPC_ERROR_CODES | Override default JSON rpc error codes that can trig
5656
ORACLE_HOME_EVENTS_REPROCESSING | If set to `true`, home events happened in the past will be refetched and processed once again, to ensure that nothing was missed on the first pass. | `bool`
5757
ORACLE_HOME_EVENTS_REPROCESSING_BATCH_SIZE | Batch size for one `eth_getLogs` request when reprocessing old logs in the home chain. Defaults to `1000` | `integer`
5858
ORACLE_HOME_EVENTS_REPROCESSING_BLOCK_DELAY | Block confirmations number, after which old logs are being reprocessed in the home chain. Defaults to `500` | `integer`
59+
ORACLE_HOME_RPC_SYNC_STATE_CHECK_INTERVAL | Interval for checking JSON RPC sync state, by requesting the latest block number. Oracle will switch to the fallback JSON RPC in case sync process is stuck | `integer`
5960
ORACLE_FOREIGN_EVENTS_REPROCESSING | If set to `true`, foreign events happened in the past will be refetched and processed once again, to ensure that nothing was missed on the first pass. | `bool`
6061
ORACLE_FOREIGN_EVENTS_REPROCESSING_BATCH_SIZE | Batch size for one `eth_getLogs` request when reprocessing old logs in the foreign chain. Defaults to `500` | `integer`
6162
ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY | Block confirmations number, after which old logs are being reprocessed in the foreign chain. Defaults to `250` | `integer`
63+
ORACLE_FOREIGN_RPC_SYNC_STATE_CHECK_INTERVAL | Interval for checking JSON RPC sync state, by requesting the latest block number. Oracle will switch to the fallback JSON RPC in case sync process is stuck | `integer`
6264

6365

6466
## Monitor configuration

oracle/config/base.config.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ const {
77
HOME_AMB_ABI,
88
FOREIGN_AMB_ABI
99
} = require('../../commons')
10-
const { web3Home, web3Foreign } = require('../src/services/web3')
10+
const {
11+
web3Home,
12+
web3Foreign,
13+
web3HomeRedundant,
14+
web3HomeFallback,
15+
web3ForeignRedundant,
16+
web3ForeignFallback,
17+
web3ForeignArchive
18+
} = require('../src/services/web3')
1119
const { add0xPrefix, privateKeyToAddress } = require('../src/utils/utils')
1220
const { EXIT_CODES } = require('../src/utils/constants')
1321

@@ -27,9 +35,11 @@ const {
2735
ORACLE_HOME_EVENTS_REPROCESSING,
2836
ORACLE_HOME_EVENTS_REPROCESSING_BATCH_SIZE,
2937
ORACLE_HOME_EVENTS_REPROCESSING_BLOCK_DELAY,
38+
ORACLE_HOME_RPC_SYNC_STATE_CHECK_INTERVAL,
3039
ORACLE_FOREIGN_EVENTS_REPROCESSING,
3140
ORACLE_FOREIGN_EVENTS_REPROCESSING_BATCH_SIZE,
32-
ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY
41+
ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY,
42+
ORACLE_FOREIGN_RPC_SYNC_STATE_CHECK_INTERVAL
3343
} = process.env
3444

3545
let homeAbi
@@ -63,9 +73,12 @@ const homeConfig = {
6373
bridgeAddress: COMMON_HOME_BRIDGE_ADDRESS,
6474
bridgeABI: homeAbi,
6575
pollingInterval: parseInt(ORACLE_HOME_RPC_POLLING_INTERVAL, 10),
76+
syncCheckInterval: parseInt(ORACLE_HOME_RPC_SYNC_STATE_CHECK_INTERVAL, 10) || 60000,
6677
startBlock: parseInt(ORACLE_HOME_START_BLOCK, 10) || 0,
6778
blockPollingLimit: parseInt(ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT, 10),
6879
web3: web3Home,
80+
web3Redundant: web3HomeRedundant,
81+
web3Fallback: web3HomeFallback,
6982
bridgeContract: homeContract,
7083
eventContract: homeContract,
7184
reprocessingOptions: {
@@ -81,9 +94,13 @@ const foreignConfig = {
8194
bridgeAddress: COMMON_FOREIGN_BRIDGE_ADDRESS,
8295
bridgeABI: foreignAbi,
8396
pollingInterval: parseInt(ORACLE_FOREIGN_RPC_POLLING_INTERVAL, 10),
97+
syncCheckInterval: parseInt(ORACLE_FOREIGN_RPC_SYNC_STATE_CHECK_INTERVAL, 10) || 60000,
8498
startBlock: parseInt(ORACLE_FOREIGN_START_BLOCK, 10) || 0,
8599
blockPollingLimit: parseInt(ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT, 10),
86100
web3: web3Foreign,
101+
web3Redundant: web3ForeignRedundant,
102+
web3Fallback: web3ForeignFallback,
103+
web3Archive: web3ForeignArchive || web3Foreign,
87104
bridgeContract: foreignContract,
88105
eventContract: foreignContract,
89106
reprocessingOptions: {
+1-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
const baseConfig = require('./base.config')
22

33
const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants')
4-
const { web3Foreign, web3ForeignRedundant, web3ForeignFallback } = require('../src/services/web3')
54

65
const { ORACLE_FOREIGN_TX_RESEND_INTERVAL } = process.env
76

87
module.exports = {
98
...baseConfig,
9+
main: baseConfig.foreign,
1010
queue: 'foreign-prioritized',
1111
id: 'foreign',
1212
name: 'sender-foreign',
13-
web3: web3Foreign,
14-
web3Redundant: web3ForeignRedundant,
15-
web3Fallback: web3ForeignFallback,
1613
resendInterval: parseInt(ORACLE_FOREIGN_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL
1714
}

oracle/config/home-sender.config.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
const baseConfig = require('./base.config')
22

33
const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants')
4-
const { web3Home, web3HomeRedundant, web3HomeFallback } = require('../src/services/web3')
54

65
const { ORACLE_HOME_TX_RESEND_INTERVAL } = process.env
76

87
module.exports = {
98
...baseConfig,
9+
main: baseConfig.home,
1010
queue: 'home-prioritized',
1111
id: 'home',
1212
name: 'sender-home',
13-
web3: web3Home,
14-
web3Redundant: web3HomeRedundant,
15-
web3Fallback: web3HomeFallback,
1613
resendInterval: parseInt(ORACLE_HOME_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL
1714
}

oracle/config/information-request-watcher.config.js

-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
const baseConfig = require('./base.config')
2-
const { web3ForeignArchive } = require('../src/services/web3')
32

43
const id = `${baseConfig.id}-information-request`
54

65
module.exports = {
76
...baseConfig,
8-
web3ForeignArchive: web3ForeignArchive || baseConfig.foreign.web3,
97
main: baseConfig.home,
108
event: 'UserRequestForInformation',
119
sender: 'home',

oracle/src/events/processAMBInformationRequests/index.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ Object.keys(asyncCalls).forEach(method => {
3535
})
3636

3737
function processInformationRequestsBuilder(config) {
38-
const { home, foreign, web3ForeignArchive } = config
38+
const { home, foreign } = config
3939

4040
let validatorContract = null
4141
let blockFinder = null
4242

43+
foreign.web3Archive.currentProvider.startSyncStateChecker(foreign.syncCheckInterval)
44+
4345
return async function processInformationRequests(informationRequests) {
4446
const txToSend = []
4547

@@ -49,13 +51,15 @@ function processInformationRequestsBuilder(config) {
4951

5052
if (blockFinder === null) {
5153
rootLogger.debug('Initializing block finder')
52-
blockFinder = await makeBlockFinder('foreign', foreign.web3)
54+
blockFinder = await makeBlockFinder('foreign', foreign.web3Archive)
5355
}
5456

57+
// latest foreign block is requested from an archive RPC, to ensure that it is synced with the network
58+
// block confirmations can be requested from the regular JSON RPC
5559
const foreignBlockNumber =
56-
(await getBlockNumber(foreign.web3)) - (await getRequiredBlockConfirmations(foreign.bridgeContract))
60+
(await getBlockNumber(foreign.web3Archive)) - (await getRequiredBlockConfirmations(foreign.bridgeContract))
5761
const homeBlock = await getBlock(home.web3, informationRequests[0].blockNumber)
58-
const lastForeignBlock = await getBlock(foreign.web3, foreignBlockNumber)
62+
const lastForeignBlock = await getBlock(foreign.web3Archive, foreignBlockNumber)
5963

6064
if (homeBlock.timestamp > lastForeignBlock.timestamp) {
6165
rootLogger.debug(
@@ -85,7 +89,7 @@ function processInformationRequestsBuilder(config) {
8589
logger.info({ requestSelector, method: asyncCallMethod, data }, 'Processing async request')
8690

8791
const call = asyncCalls[asyncCallMethod]
88-
let [status, result] = await call(web3ForeignArchive, data, foreignClosestBlock).catch(e => {
92+
let [status, result] = await call(foreign.web3Archive, data, foreignClosestBlock).catch(e => {
8993
if (e instanceof HttpListProviderError) {
9094
throw e
9195
}

oracle/src/sender.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ if (process.argv.length < 3) {
3232

3333
const config = require(path.join('../config/', process.argv[2]))
3434

35-
const { web3, web3Fallback } = config
36-
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.web3Redundant : web3
35+
const { web3, web3Fallback, syncCheckInterval } = config.main
36+
const web3Redundant = ORACLE_TX_REDUNDANCY === 'true' ? config.main.web3Redundant : web3
3737

3838
const nonceKey = `${config.id}:nonce`
3939
let chainId = 0
@@ -43,6 +43,7 @@ async function initialize() {
4343
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
4444

4545
web3.currentProvider.urls.forEach(checkHttps(config.id))
46+
web3.currentProvider.startSyncStateChecker(syncCheckInterval)
4647

4748
GasPrice.start(config.id, web3)
4849

oracle/src/services/HttpListProvider.js

+41-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const fetch = require('node-fetch')
22
const promiseRetry = require('promise-retry')
3+
const { utils } = require('web3')
34
const { FALLBACK_RPC_URL_SWITCH_TIMEOUT } = require('../utils/constants')
45

56
const { onInjected } = require('./injectedLogger')
@@ -39,19 +40,54 @@ function HttpListProvider(urls, options = {}) {
3940
this.options = { ...defaultOptions, ...options }
4041
this.currentIndex = 0
4142
this.lastTimeUsedPrimary = 0
43+
this.latestBlock = 0
44+
this.syncStateCheckerIntervalId = 0
4245

4346
onInjected(logger => {
4447
this.logger = logger.child({ module: `HttpListProvider:${this.options.name}` })
4548
})
4649
}
4750

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)
5154
}
55+
}
5256

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) {
5385
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+
5591
this.logger.info(
5692
{ index: newIndex, oldURL: this.urls[prevIndex], newURL: this.urls[newIndex] },
5793
'Switching to fallback JSON-RPC URL'
@@ -80,11 +116,7 @@ HttpListProvider.prototype.send = async function send(payload, callback) {
80116

81117
// if some of URLs failed to respond, current URL index is updated to the first URL that responded
82118
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)
88120
}
89121
callback(null, result)
90122
} catch (e) {

oracle/src/utils/constants.js

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ module.exports = {
2727
MIN_GAS_PRICE_BUMP_FACTOR: 0.1,
2828
DEFAULT_TRANSACTION_RESEND_INTERVAL: 20 * 60 * 1000,
2929
FALLBACK_RPC_URL_SWITCH_TIMEOUT: 60 * 60 * 1000,
30-
BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT: 10,
3130
SENDER_QUEUE_MAX_PRIORITY: 10,
3231
SENDER_QUEUE_SEND_PRIORITY: 5,
3332
SENDER_QUEUE_CHECK_STATUS_PRIORITY: 1,

oracle/src/watcher.js

+4-30
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ const logger = require('./services/logger')
66
const { getShutdownFlag } = require('./services/shutdownState')
77
const { getBlockNumber, getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
88
const { checkHTTPS, watchdog } = require('./utils/utils')
9-
const {
10-
EXIT_CODES,
11-
BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT,
12-
MAX_HISTORY_BLOCK_TO_REPROCESS
13-
} = require('./utils/constants')
9+
const { EXIT_CODES, MAX_HISTORY_BLOCK_TO_REPROCESS } = require('./utils/constants')
1410

1511
if (process.argv.length < 3) {
1612
logger.error('Please check the number of arguments, config file was not provided')
@@ -38,21 +34,21 @@ const {
3834
pollingInterval,
3935
chain,
4036
reprocessingOptions,
41-
blockPollingLimit
37+
blockPollingLimit,
38+
syncCheckInterval
4239
} = config.main
4340
const lastBlockRedisKey = `${config.id}:lastProcessedBlock`
4441
const lastReprocessedBlockRedisKey = `${config.id}:lastReprocessedBlock`
4542
const seenEventsRedisKey = `${config.id}:seenEvents`
4643
let lastProcessedBlock = Math.max(startBlock - 1, 0)
4744
let lastReprocessedBlock
48-
let lastSeenBlockNumber = 0
49-
let sameBlockNumberCounter = 0
5045

5146
async function initialize() {
5247
try {
5348
const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger)
5449

5550
web3.currentProvider.urls.forEach(checkHttps(chain))
51+
web3.currentProvider.startSyncStateChecker(syncCheckInterval)
5652

5753
await getLastProcessedBlock()
5854
await getLastReprocessedBlock()
@@ -225,28 +221,6 @@ async function getLastBlockToProcess(web3, bridgeContract) {
225221
getBlockNumber(web3),
226222
getRequiredBlockConfirmations(bridgeContract)
227223
])
228-
229-
if (lastBlockNumber < lastSeenBlockNumber) {
230-
sameBlockNumberCounter = 0
231-
logger.warn({ lastBlockNumber, lastSeenBlockNumber }, 'Received block number less than already seen block')
232-
web3.currentProvider.switchToFallbackRPC()
233-
} else if (lastBlockNumber === lastSeenBlockNumber) {
234-
sameBlockNumberCounter++
235-
if (sameBlockNumberCounter > 1) {
236-
logger.info({ lastBlockNumber, sameBlockNumberCounter }, 'Received the same block number more than twice')
237-
if (sameBlockNumberCounter >= BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT) {
238-
sameBlockNumberCounter = 0
239-
logger.warn(
240-
{ lastBlockNumber, n: BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT },
241-
'Received the same block number for too many times. Probably node is not synced anymore'
242-
)
243-
web3.currentProvider.switchToFallbackRPC()
244-
}
245-
}
246-
} else {
247-
sameBlockNumberCounter = 0
248-
lastSeenBlockNumber = lastBlockNumber
249-
}
250224
return lastBlockNumber - requiredBlockConfirmations
251225
}
252226

0 commit comments

Comments
 (0)