UNPKG

@vechain/sdk-network

Version:

This module serves as the standard interface connecting decentralized applications (dApps) and users to the VeChainThor blockchain

98 lines (87 loc) 3.63 kB
import { InvalidDataType } from '@vechain/sdk-errors'; import { NODE_HEALTHCHECK_TOLERANCE_IN_SECONDS, thorest } from '../../utils'; import { type BlocksModule, type CompressedBlockDetail } from '../blocks'; import { type ConnectedPeer } from './types'; import { HttpMethod } from '../../http'; /** * The `NodesModule` class serves as a module for node-related functionality, for example, checking the health of a node. */ class NodesModule { readonly blocksModule: BlocksModule; constructor(blocksModule: BlocksModule) { this.blocksModule = blocksModule; } /** * Retrieves connected peers of a node. * * @returns A promise that resolves to the list of connected peers. */ public async getNodes(): Promise<ConnectedPeer[]> { const nodes = (await this.blocksModule.httpClient.http( HttpMethod.GET, thorest.nodes.get.NODES() )) as ConnectedPeer[] | null; return nodes ?? []; } /** * Checks the health of a node using the following algorithm: * 1. Make an HTTP GET request to retrieve the last block timestamp. * 2. Calculates the difference between the current time and the last block timestamp. * 3. If the difference is less than the tolerance, the node is healthy. * Note, we could also check '/node/network/peers since' but the difficulty with this approach is * if you consider a scenario where the node is connected to 20+ peers, which is healthy, and it receives the new blocks as expected. * But what if the node's disk is full, and it's not writing the new blocks to its database? In this case the node is off-sync even * though it's technically alive and connected * @returns A boolean indicating whether the node is healthy. * @throws {InvalidDataTypeError} */ public async isHealthy(): Promise<boolean> { /** * @internal * Perform an HTTP GET request using the SimpleNet instance to get the latest block */ const response = await this.blocksModule.getBestBlockCompressed(); /** * timestamp from the last block and, eventually handle errors * @internal */ const lastBlockTimestamp: number = this.getTimestampFromBlock(response); /** * seconds elapsed since the timestamp of the last block * @internal */ const secondsSinceLastBlock = Math.floor(Date.now() / 1000) - lastBlockTimestamp; return ( Math.abs(secondsSinceLastBlock) < NODE_HEALTHCHECK_TOLERANCE_IN_SECONDS ); } /** * Extracts the timestamp from the block * @remarks * This function throws an error if the timestamp key does not exist in the response from the API call to the node * @param response the response from the API call to the node * @returns the timestamp from the block * @throws{InvalidDataType} */ private readonly getTimestampFromBlock = ( response: CompressedBlockDetail | null ): number => { if ( response === null || response === undefined || typeof response !== 'object' || !('timestamp' in response) || typeof response.timestamp !== 'number' ) { throw new InvalidDataType( 'NodesModule.getTimestampFromBlock()', 'Sending failed: Input must be a valid raw transaction in hex format.', { response } ); } return response?.timestamp; }; } export { NodesModule };