Skip to content

Commit

Permalink
Creator node availability verification route (#1013)
Browse files Browse the repository at this point in the history
Availability verification route intended for use by service providers deploying new nodes
  • Loading branch information
hareeshnagaraj committed Oct 30, 2020
1 parent 53988c9 commit 7183909
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 2 deletions.
58 changes: 58 additions & 0 deletions creator-node/scripts/verifyHealthCheckDuration.js
@@ -0,0 +1,58 @@
const axios = require('axios')
const { generateTimestampAndSignature } = require('../src/apiSigning')
const { promisify } = require('util')

const crypto = require('crypto')

const PRIVATE_KEY = process.env.delegatePrivateKey
const CREATOR_NODE_ENDPOINT = process.env.creatorNodeEndpoint
const randomBytes = promisify(crypto.randomBytes)

/**
* Process command line args and issue duration health check
*/
async function run () {
try {
parseEnvVarsAndArgs()
} catch (e) {
console.error(`\nIncorrect script usage: ${e.message}`)
console.error(`Script usage: node verifyHealthcheckDuration.js`)
return
}

try {
// Generate signature using local key
const randomBytesToSign = (await randomBytes(18)).toString()
const signedLocalData = generateTimestampAndSignature({ randomBytesToSign }, PRIVATE_KEY)
// Add randomBytes to outgoing request parameters
const reqParam = signedLocalData
reqParam.randomBytes = randomBytesToSign
let requestConfig = {
url: `${CREATOR_NODE_ENDPOINT}/health_check/duration`,
method: 'get',
params: reqParam,
responseType: 'json'
}
let resp = await axios(requestConfig)
let data = resp.data
console.dir(data, { depth: 5 })
} catch (e) {
console.error(e)
}
}

/**
* Parses the environment variables and command line args
* export creatorNodeEndpoint=http://cn1_creator-node_1:4000
* export delegatePrivateKey=f0b743ce8adb7938f1212f188347a63...
* NOTE: DO NOT PREFIX PRIVATE KEY WITH 0x
*/
function parseEnvVarsAndArgs () {
if (!CREATOR_NODE_ENDPOINT || !PRIVATE_KEY) {
let errorMsg = `creatorNodeEndpoint [${CREATOR_NODE_ENDPOINT}] or delegatePrivateKey [${PRIVATE_KEY}] have not been exported. `
errorMsg += "Please export environment variables 'delegatePrivateKey' and 'creatorNodeEndpoint'."
throw new Error(errorMsg)
}
}

run()
@@ -1,5 +1,6 @@
const versionInfo = require('../../../.version.json')
const config = require('../../config')
const utils = require('../../utils.js')

/**
* Perform a basic health check, returning the
Expand Down Expand Up @@ -32,6 +33,19 @@ const healthCheck = async ({ libs } = {}, logger, sequelize) => {
return response
}

/**
* Perform a duration health check limited to configured delegateOwnerWalet
* Used to validate availability prior to joiing the network
* @param {*} ServiceRegistry
* @param {*} logger
*/
const healthCheckDuration = async () => {
// Wait 5 minutes, intentionally holding this route open
await utils.timeout(300000)
return { success: true }
}

module.exports = {
healthCheck
healthCheck,
healthCheckDuration
}
32 changes: 31 additions & 1 deletion creator-node/src/components/healthCheck/healthCheckController.js
@@ -1,12 +1,19 @@
const express = require('express')
const { handleResponse, successResponse } = require('../../apiHelpers')
const { healthCheck } = require('./healthCheckComponentService')
const { healthCheck, healthCheckDuration } = require('./healthCheckComponentService')
const { syncHealthCheck } = require('./syncHealthCheckComponentService')
const { serviceRegistry } = require('../../serviceRegistry')
const { sequelize } = require('../../models')

const { recoverWallet } = require('../../apiSigning')

const config = require('../../config')

const router = express.Router()

// 5 minutes in ms is the maximum age of a timestamp sent to /health_check/duration
const MAX_HEALTH_CHECK_TIMESTAMP_AGE_MS = 300000

// Controllers

/**
Expand All @@ -28,9 +35,32 @@ const syncHealthCheckController = async () => {
return successResponse(response)
}

/**
* Controller for health_check/duration route
* Calls healthCheckComponentService
*/
const healthCheckDurationController = async (req) => {
let { timestamp, randomBytes, signature } = req.query
const recoveryObject = { randomBytesToSign: randomBytes, timestamp }
const recoveredPublicWallet = recoverWallet(recoveryObject, signature).toLowerCase()
const recoveredTimestampDate = new Date(timestamp)
const currentTimestampDate = new Date()
const requestAge = currentTimestampDate - recoveredTimestampDate
if (requestAge >= MAX_HEALTH_CHECK_TIMESTAMP_AGE_MS) {
throw new Error(`Submitted timestamp=${recoveredTimestampDate}, current timestamp=${currentTimestampDate}. Maximum age =${MAX_HEALTH_CHECK_TIMESTAMP_AGE_MS}`)
}
const delegateOwnerWallet = config.get('delegateOwnerWallet').toLowerCase()
if (recoveredPublicWallet !== delegateOwnerWallet) {
throw new Error("Requester's public key does does not match Creator Node's delegate owner wallet.")
}
let response = await healthCheckDuration()
return successResponse(response)
}

// Routes

router.get('/health_check', handleResponse(healthCheckController))
router.get('/health_check/sync', handleResponse(syncHealthCheckController))
router.get('/health_check/duration', handleResponse(healthCheckDurationController))

module.exports = router

0 comments on commit 7183909

Please sign in to comment.