Skip to content

Commit

Permalink
Merge pull request #245 from lidofinance/develop
Browse files Browse the repository at this point in the history
Dev to main
  • Loading branch information
Tarens2 authored Sep 2, 2024
2 parents da97145 + 64bccae commit 4072905
Show file tree
Hide file tree
Showing 17 changed files with 221 additions and 40 deletions.
36 changes: 27 additions & 9 deletions src/app/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Inject, Injectable, LoggerService, OnModuleInit } from '@nestjs/common';
import { LOGGER_PROVIDER } from '@lido-nestjs/logger';

import { ConfigService } from 'common/config';
import { ConfigService, ENV_KEYS, EnvironmentVariables } from 'common/config';
import { PrometheusService } from 'common/prometheus';
import { ConsensusProviderService } from 'common/consensus-provider';
import { ExecutionProviderService } from 'common/execution-provider';
import { APP_NAME, APP_VERSION } from './app.constants';
import { commonPatterns, satanizer } from '@lidofinance/satanizer';

@Injectable()
export class AppService implements OnModuleInit {
Expand All @@ -20,14 +21,8 @@ export class AppService implements OnModuleInit {

public async onModuleInit(): Promise<void> {
await this.validateNetwork();

const network = await this.executionProviderService.getNetworkName();
const env = this.configService.get('NODE_ENV');
const version = APP_VERSION;
const name = APP_NAME;

this.prometheusService.buildInfo.labels({ env, network, name, version }).inc();
this.logger.log('Init app', { env, network, name, version });
await this.prometheusBuildInfoMetrics();
this.prometheusEnvsInfoMetrics();
}

/**
Expand All @@ -43,4 +38,27 @@ export class AppService implements OnModuleInit {
throw new Error('Chain ids do not match');
}
}

protected async prometheusBuildInfoMetrics() {
const network = await this.executionProviderService.getNetworkName();
const env = this.configService.get('NODE_ENV');
const version = APP_VERSION;
const name = APP_NAME;

this.prometheusService.buildInfo.labels({ env, network, name, version }).inc();
this.logger.log('Init app', { env, network, name, version });
}

protected prometheusEnvsInfoMetrics() {
const secrets = this.configService.secrets;
const mask = satanizer([...commonPatterns, ...secrets]);

const allConfigEnvs = {};
ENV_KEYS.forEach((key: keyof EnvironmentVariables) => {
allConfigEnvs[key] = mask(this.configService.get(key));
});

this.prometheusService.envsInfo.labels(allConfigEnvs).inc();
this.logger.log('Init app dumping envs', allConfigEnvs);
}
}
15 changes: 8 additions & 7 deletions src/common/config/env.validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class EnvironmentVariables {
@IsNumber()
@Min(1)
@Transform(toNumber({ defaultValue: 3000 }))
PORT: number;
PORT: number = 3000;

@IsOptional()
@IsString()
Expand Down Expand Up @@ -49,16 +49,16 @@ export class EnvironmentVariables {
@IsOptional()
@IsEnum(LogLevel)
@Transform(({ value }) => value || LogLevel.info)
LOG_LEVEL: LogLevel;
LOG_LEVEL: LogLevel = null;

@IsOptional()
@IsEnum(LogFormat)
@Transform(({ value }) => value || LogFormat.json)
LOG_FORMAT: LogFormat;
LOG_FORMAT: LogFormat = null;

@IsOptional()
@IsString()
JOB_INTERVAL_VALIDATORS;
JOB_INTERVAL_VALIDATORS = null;

@IsOptional()
@IsString()
Expand All @@ -71,17 +71,18 @@ export class EnvironmentVariables {
@IsArray()
@ArrayMinSize(1)
@Transform(({ value }) => value.split(','))
CL_API_URLS!: string[];
CL_API_URLS: string[] = null;

@IsArray()
@ArrayMinSize(1)
@Transform(({ value }) => value.split(','))
EL_RPC_URLS!: string[];
EL_RPC_URLS: string[] = null;

@IsNumber()
@Transform(({ value }) => Number(value))
CHAIN_ID!: number;
CHAIN_ID: number = null;
}
export const ENV_KEYS = Object.keys(new EnvironmentVariables());

export function validate(config: Record<string, unknown>) {
const validatedConfig = plainToClass(EnvironmentVariables, config);
Expand Down
25 changes: 23 additions & 2 deletions src/common/health/health.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import { Module } from '@nestjs/common';
import { Inject, LoggerService, Module, OnModuleInit } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { HealthController } from './health.controller';
import { ExecutionProviderHealthIndicator } from './execution-provider.indicator';
import { ConsensusProviderIndicator } from './consensus-provider.indicator';
import { GenesisTimeModule } from '../genesis-time';
import { LOGGER_PROVIDER } from '@lido-nestjs/logger';

@Module({
providers: [ExecutionProviderHealthIndicator, ConsensusProviderIndicator],
controllers: [HealthController],
imports: [TerminusModule, GenesisTimeModule],
})
export class HealthModule {}
export class HealthModule implements OnModuleInit {
constructor(
@Inject(LOGGER_PROVIDER) protected readonly logger: LoggerService,
protected readonly consensusProviderIndicator: ConsensusProviderIndicator,
protected readonly executionProviderIndicator: ExecutionProviderHealthIndicator,
) {}

async onModuleInit() {
await this.startUpChecks();
}

async startUpChecks() {
try {
await this.consensusProviderIndicator.isHealthy('consensusProvider');
await this.executionProviderIndicator.isHealthy('executionProvider');
this.logger.log(`Start up checks passed successfully`);
} catch (e) {
this.logger.error(`Start up checks failed with error: ${e}`);
}
}
}
15 changes: 14 additions & 1 deletion src/common/prometheus/prometheus.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getOrCreateMetric } from '@willsoto/nestjs-prometheus';
import { Options, Metrics, Metric } from './interfaces';
import { METRICS_PREFIX } from './prometheus.constants';
import { RequestSourceType } from '../../http/request-time/headers/request-source-type';
import { ENV_KEYS } from '../config';

export class PrometheusService {
protected prefix = METRICS_PREFIX;
Expand All @@ -25,7 +26,19 @@ export class PrometheusService {
public buildInfo = this.getOrCreateMetric('Gauge', {
name: 'build_info',
help: 'Build information',
labelNames: ['name', 'version', 'env', 'network', 'startSlot'],
labelNames: ['name', 'version', 'env', 'network'],
});

public envsInfo = this.getOrCreateMetric('Gauge', {
name: METRICS_PREFIX + 'envs_info',
help: 'Environment variables information',
labelNames: ENV_KEYS,
});

public validatorsState = this.getOrCreateMetric('Gauge', {
name: METRICS_PREFIX + 'validators_state',
help: 'balances of Lido validators with withdrawable_epoch by frames',
labelNames: ['frame', 'balance'],
});

public clApiRequestDuration = this.getOrCreateMetric('Histogram', {
Expand Down
9 changes: 9 additions & 0 deletions src/common/validators/strigify-frame-balances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BigNumber } from '@ethersproject/bignumber';

export function stringifyFrameBalances(frameBalances: Record<string, BigNumber>) {
return JSON.stringify(
Object.keys(frameBalances).reduce((acc, key) => {
return { ...acc, [key]: frameBalances[key].toString() };
}, {}),
);
}
7 changes: 2 additions & 5 deletions src/http/common/middleware/logger.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Inject, Injectable, LoggerService, NestMiddleware } from '@nestjs/common';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import { Request, Reply } from './interfaces';
import { FastifyRequest } from 'fastify';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
Expand All @@ -10,15 +9,13 @@ export class LoggerMiddleware implements NestMiddleware {
private readonly logger: LoggerService,
) {}

use(request: any, reply: Reply, next: () => void) {
use(request: Request, reply: Reply, next: () => void) {
const { ip, method, headers, originalUrl } = request;
const userAgent = headers['user-agent'] ?? '';

const ips = request.ips ? request.ips : [];

reply.on('finish', () => {
const { statusCode } = reply;
const log = { method, originalUrl, statusCode, userAgent, ip, ips };
const log = { method, originalUrl, statusCode, userAgent, ip };

this.logger.log(JSON.stringify(log));
});
Expand Down
1 change: 1 addition & 0 deletions src/http/http.constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const HTTP_PATHS = {
1: {
nft: 'nft',
'validators-info': 'validators-info',
'request-time': 'request-time',
'estimate-gas': 'estimate-gas',
},
Expand Down
3 changes: 2 additions & 1 deletion src/http/http.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { CacheModule, CacheControlHeadersInterceptor } from './common/cache';
import { RequestTimeModule } from './request-time';
import { NFTModule } from './nft';
import { EstimateModule } from './estimate';
import { ValidatorsModule } from './validators';

@Module({
imports: [RequestTimeModule, NFTModule, EstimateModule, CacheModule, ThrottlerModule],
imports: [RequestTimeModule, NFTModule, EstimateModule, ValidatorsModule, CacheModule, ThrottlerModule],
providers: [
{ provide: APP_GUARD, useClass: ThrottlerBehindProxyGuard },
{ provide: APP_INTERCEPTOR, useClass: CacheControlHeadersInterceptor },
Expand Down
1 change: 1 addition & 0 deletions src/http/validators/dto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './validators.dto';
33 changes: 33 additions & 0 deletions src/http/validators/dto/validators.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ApiProperty } from '@nestjs/swagger';

export class ValidatorsDto {
@ApiProperty({
example: 1658650005,
description: 'ms time when data was last updated at',
})
lastUpdatedAt: number;

@ApiProperty({
example: 1724856617,
description: 'max exit epoch over all CL network',
})
maxExitEpoch: number;

@ApiProperty({
example: '{}',
description: 'sum of balances Lido validators with withdrawable_epoch by frame',
})
frameBalances: Record<string, string>;

@ApiProperty({
example: 100000,
description: 'total number of validators in network',
})
totalValidators: number;

@ApiProperty({
example: 100000,
description: 'current frame',
})
currentFrame: number;
}
3 changes: 3 additions & 0 deletions src/http/validators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './validators.controller';
export * from './validators.module';
export * from './validators.service';
28 changes: 28 additions & 0 deletions src/http/validators/validators.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
ClassSerializerInterceptor,
Controller,
Get,
HttpStatus,
UseInterceptors,
Version,
CacheTTL,
} from '@nestjs/common';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
import { HTTP_PATHS } from 'http/http.constants';
import { ValidatorsService } from './validators.service';
import { ValidatorsDto } from './dto';

@Controller()
@ApiTags('Validators')
@UseInterceptors(ClassSerializerInterceptor)
export class ValidatorsController {
constructor(protected readonly validatorsService: ValidatorsService) {}

@Version('1')
@Get(HTTP_PATHS[1]['validators-info'])
@CacheTTL(20 * 1000)
@ApiResponse({ status: HttpStatus.OK, type: ValidatorsDto })
async validatorsV1(): Promise<ValidatorsDto> {
return this.validatorsService.getValidatorsInfo();
}
}
13 changes: 13 additions & 0 deletions src/http/validators/validators.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from 'common/config';
import { ValidatorsController } from './validators.controller';
import { ValidatorsService } from './validators.service';
import { ValidatorsStorageModule } from '../../storage';
import { GenesisTimeModule } from '../../common/genesis-time';

@Module({
imports: [ConfigModule, ValidatorsStorageModule, GenesisTimeModule],
controllers: [ValidatorsController],
providers: [ValidatorsService],
})
export class ValidatorsModule {}
34 changes: 34 additions & 0 deletions src/http/validators/validators.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from 'common/config';
import { ValidatorsStorageService } from '../../storage';
import { GenesisTimeService } from '../../common/genesis-time';

@Injectable()
export class ValidatorsService {
constructor(
protected readonly configService: ConfigService,
protected readonly validatorsServiceStorage: ValidatorsStorageService,
protected readonly genesisTimeService: GenesisTimeService,
) {}

getValidatorsInfo() {
const lastUpdatedAt = this.validatorsServiceStorage.getLastUpdate();
const maxExitEpoch = Number(this.validatorsServiceStorage.getMaxExitEpoch());
const frameBalancesBigNumber = this.validatorsServiceStorage.getFrameBalances();
const totalValidators = this.validatorsServiceStorage.getTotal();
const currentFrame = this.genesisTimeService.getFrameOfEpoch(this.genesisTimeService.getCurrentEpoch());

const frameBalances = Object.keys(frameBalancesBigNumber).reduce((acc, item) => {
acc[item] = frameBalancesBigNumber[item].toString();
return acc;
}, {} as Record<string, string>);

return {
lastUpdatedAt,
maxExitEpoch,
frameBalances,
totalValidators,
currentFrame,
};
}
}
Loading

0 comments on commit 4072905

Please sign in to comment.