Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add watchdog for watchers #106

Merged
merged 10 commits into from
Oct 25, 2018
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ HOME_START_BLOCK=
FOREIGN_START_BLOCK=

LOG_LEVEL=debug
MAX_PROCESSING_TIME=20000

fvictorio marked this conversation as resolved.
Show resolved Hide resolved
#testing accs
USER_ADDRESS=0x59c4474184579b9c31b5e51445b6eef91cebf370
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ where the _watcher_ could be one of:
| `REDIS_LOCK_TTL` | Threshold in milliseconds for locking a resource in the Redis DB. Until the threshold is exceeded, the resource is unlocked. Usually it is `1000`. | integer |
| `ALLOW_HTTP` | **Only use in test environments - must be omitted in production environments.**. If this parameter is specified and set to `yes`, RPC URLs can be specified in form of HTTP links. A warning that the connection is insecure will be written to the logs. | `yes` / `no` |
| `LOG_LEVEL` | Set the level of details in the logs. | `trace` / `debug` / `info` / `warn` / `error` / `fatal` |
| `MAX_PROCESSING_TIME` | The workers processes will be killed if this amount of time (in milliseconds) is ellapsed before they finish processing. It is recommended to set this value to 4 times the value of the longest polling time (set with the `HOME_POLLING_INTERVAL` and `FOREIGN_POLLING_INTERVAL` variables). To disable this, set the time to 0. | integer |

### Useful Commands for Development

Expand Down
13 changes: 12 additions & 1 deletion config/base.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,24 @@ switch (process.env.BRIDGE_MODE) {
}
}

let maxProcessingTime = null
if (String(process.env.MAX_PROCESSING_TIME) === '0') {
maxProcessingTime = 0
} else if (!process.env.MAX_PROCESSING_TIME) {
maxProcessingTime =
4 * Math.max(process.env.HOME_POLLING_INTERVAL, process.env.FOREIGN_POLLING_INTERVAL)
} else {
maxProcessingTime = Number(process.env.MAX_PROCESSING_TIME)
}

const bridgeConfig = {
homeBridgeAddress: process.env.HOME_BRIDGE_ADDRESS,
homeBridgeAbi: homeAbi,
foreignBridgeAddress: process.env.FOREIGN_BRIDGE_ADDRESS,
foreignBridgeAbi: foreignAbi,
eventFilter: {},
validatorAddress: VALIDATOR_ADDRESS || privateKeyToAddress(VALIDATOR_ADDRESS_PRIVATE_KEY)
validatorAddress: VALIDATOR_ADDRESS || privateKeyToAddress(VALIDATOR_ADDRESS_PRIVATE_KEY),
maxProcessingTime
}

const homeConfig = {
Expand Down
2 changes: 2 additions & 0 deletions config/foreign-sender.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require('dotenv').config()
const baseConfig = require('./base.config')

const { web3Foreign } = require('../src/services/web3')

module.exports = {
...baseConfig.bridgeConfig,
queue: 'foreign',
id: 'foreign',
name: 'sender-foreign',
Expand Down
2 changes: 2 additions & 0 deletions config/home-sender.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require('dotenv').config()
const baseConfig = require('./base.config')

const { web3Home } = require('../src/services/web3')

module.exports = {
...baseConfig.bridgeConfig,
queue: 'home',
id: 'home',
name: 'sender-home',
Expand Down
3 changes: 2 additions & 1 deletion scripts/privateKeyToAddress.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ require('dotenv').config({
path: path.join(__dirname, '..', '.env')
})
const { privateKeyToAddress } = require('../src/utils/utils')
const { EXIT_CODES } = require('../src/utils/constants')

const privateKey = process.env.VALIDATOR_ADDRESS_PRIVATE_KEY

if (!privateKey) {
console.error('Environment variable VALIDATOR_ADDRESS_PRIVATE_KEY is not set')
process.exit(1)
process.exit(EXIT_CODES.GENERAL_ERROR)
}

const address = privateKeyToAddress(privateKey)
Expand Down
3 changes: 2 additions & 1 deletion scripts/resetLastBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require('dotenv').config({
path: path.join(__dirname, '../.env')
})
const { id } = require('../config/base.config')
const { EXIT_CODES } = require('../src/utils/constants')

const redis = new Redis(process.env.REDIS_URL)

Expand All @@ -22,7 +23,7 @@ if (process.argv.length < 4) {

function logError(message) {
console.log(message)
process.exit(1)
process.exit(EXIT_CODES.GENERAL_ERROR)
}

function getRedisKey(name) {
Expand Down
4 changes: 2 additions & 2 deletions src/events/processAffirmationRequests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const rootLogger = require('../../services/logger')
const { web3Home } = require('../../services/web3')
const promiseLimit = require('promise-limit')
const bridgeValidatorsABI = require('../../../abis/BridgeValidators.abi')
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const estimateGas = require('./estimateGas')
const {
AlreadyProcessedError,
Expand Down Expand Up @@ -64,7 +64,7 @@ function processAffirmationRequestsBuilder(config) {
)
} else if (e instanceof InvalidValidatorError) {
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
process.exit(10)
process.exit(EXIT_CODES.INCOMPATIBILITY)
} else if (e instanceof AlreadySignedError) {
logger.info(`Already signed affirmationRequest ${affirmationRequest.transactionHash}`)
return
Expand Down
4 changes: 2 additions & 2 deletions src/events/processSignatureRequests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const {
AlreadySignedError,
InvalidValidatorError
} = require('../../utils/errors')
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')

const { VALIDATOR_ADDRESS_PRIVATE_KEY } = process.env

Expand Down Expand Up @@ -81,7 +81,7 @@ function processSignatureRequestsBuilder(config) {
)
} else if (e instanceof InvalidValidatorError) {
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
process.exit(10)
process.exit(EXIT_CODES.INCOMPATIBILITY)
} else if (e instanceof AlreadySignedError) {
logger.info(`Already signed signatureRequest ${signatureRequest.transactionHash}`)
return
Expand Down
4 changes: 2 additions & 2 deletions src/events/processTransfers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const {
AlreadySignedError,
InvalidValidatorError
} = require('../../utils/errors')
const { MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const { EXIT_CODES, MAX_CONCURRENT_EVENTS } = require('../../utils/constants')
const estimateGas = require('../processAffirmationRequests/estimateGas')

const limit = promiseLimit(MAX_CONCURRENT_EVENTS)
Expand Down Expand Up @@ -61,7 +61,7 @@ function processTransfersBuilder(config) {
)
} else if (e instanceof InvalidValidatorError) {
logger.fatal({ address: config.validatorAddress }, 'Invalid validator')
process.exit(10)
process.exit(EXIT_CODES.INCOMPATIBILITY)
} else if (e instanceof AlreadySignedError) {
logger.info(`Already signed transfer ${transfer.transactionHash}`)
return
Expand Down
20 changes: 15 additions & 5 deletions src/sender.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ const { getNonce, getChainId } = require('./tx/web3')
const {
addExtraGas,
checkHTTPS,
privateKeyToAddress,
syncForEach,
waitForFunds,
privateKeyToAddress
watchdog
} = require('./utils/utils')
const { EXTRA_GAS_PERCENTAGE } = require('./utils/constants')
const { EXIT_CODES, EXTRA_GAS_PERCENTAGE } = require('./utils/constants')

const { VALIDATOR_ADDRESS_PRIVATE_KEY, REDIS_LOCK_TTL } = process.env

fvictorio marked this conversation as resolved.
Show resolved Hide resolved
const VALIDATOR_ADDRESS = privateKeyToAddress(VALIDATOR_ADDRESS_PRIVATE_KEY)

if (process.argv.length < 3) {
logger.error('Please check the number of arguments, config file was not provided')
process.exit(1)
process.exit(EXIT_CODES.GENERAL_ERROR)
}

const config = require(path.join('../config/', process.argv[2]))
Expand All @@ -44,11 +45,20 @@ async function initialize() {
chainId = await getChainId(web3Instance)
connectSenderToQueue({
queueName: config.queue,
cb: main
cb: options => {
if (config.maxProcessingTime) {
return watchdog(() => main(options), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
}

return main(options)
}
})
} catch (e) {
logger.error(e.message)
akolotov marked this conversation as resolved.
Show resolved Hide resolved
process.exit(1)
process.exit(EXIT_CODES.GENERAL_ERROR)
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ module.exports = {
maxTimeout: 360000,
randomize: true
},
DEFAULT_UPDATE_INTERVAL: 600000
DEFAULT_UPDATE_INTERVAL: 600000,
EXIT_CODES: {
GENERAL_ERROR: 1,
INCOMPATIBILITY: 10,
MAX_TIME_REACHED: 11
}
}
18 changes: 18 additions & 0 deletions src/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ function setIntervalAndRun(f, interval) {
return handler
}

/**
* Run function `f` and return its result, unless `timeout` milliseconds pass before `f` ends. If that happens, run
* `kill` instead.
*
* @param {Function} f The function to run. It is assumed that it's an async function.
* @param {Number} timeout Max time in milliseconds to wait for `f` to finish.
* @param {Function} kill Function that will be called if `f` takes more than `timeout` milliseconds.
*/
async function watchdog(f, timeout, kill) {
const timeoutHandler = setTimeout(kill, timeout)

const result = await f()
clearTimeout(timeoutHandler)

return result
}

function add0xPrefix(s) {
if (s.indexOf('0x') === 0) {
return s
Expand All @@ -85,5 +102,6 @@ module.exports = {
waitForFunds,
addExtraGas,
setIntervalAndRun,
watchdog,
privateKeyToAddress
}
16 changes: 12 additions & 4 deletions src/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ const { redis } = require('./services/redisClient')
const logger = require('./services/logger')
const rpcUrlsManager = require('./services/getRpcUrlsManager')
const { getRequiredBlockConfirmations, getEvents } = require('./tx/web3')
const { checkHTTPS } = require('./utils/utils')
const { checkHTTPS, watchdog } = require('./utils/utils')
const { EXIT_CODES } = require('./utils/constants')

if (process.argv.length < 3) {
logger.error('Please check the number of arguments, config file was not provided')
fvictorio marked this conversation as resolved.
Show resolved Hide resolved
process.exit(1)
process.exit(EXIT_CODES.GENERAL_ERROR)
}

const config = require(path.join('../config/', process.argv[2]))
Expand Down Expand Up @@ -44,14 +45,21 @@ async function initialize() {
})
} catch (e) {
logger.error(e)
process.exit(1)
process.exit(EXIT_CODES.GENERAL_ERROR)
}
}

async function runMain({ sendToQueue }) {
fvictorio marked this conversation as resolved.
Show resolved Hide resolved
try {
if (connection.isConnected() && redis.status === 'ready') {
await main({ sendToQueue })
if (config.maxProcessingTime) {
await watchdog(() => main({ sendToQueue }), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
} else {
await main({ sendToQueue })
}
}
} catch (e) {
logger.error(e)
Expand Down