Skip to content

Commit

Permalink
Merge pull request #185 from klayrHQ/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Theezr authored Dec 1, 2024
2 parents 1686376 + 748c7f2 commit d71026c
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 64 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Run tests
- name: Run tests unit
run: npm run test

- name: Run tests e2e
run: npm run test:e2e
10 changes: 8 additions & 2 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export const GLOBAL_PREFIX = 'api/v1';
////////////////////////////

export const RETRY_TIMEOUT = 5000; // 5 sec
export const BLOCKS_TO_CACHE_TOKEN_SUMMARY = 100;
export const CACHED_MODELS_ID = 1;

////////////////////////////
Expand Down Expand Up @@ -128,6 +127,13 @@ export const MAX_APPS_TO_FETCH = 100;
////////////////////////////

export const LOCATION_API_URL = 'http://ip-api.com/json';
export const BLOCKS_TO_SAVE_NETWORK_PEERS = 100;
export const DEFAULT_NETWORK_PEERS_TO_FETCH = 10;
export const MAX_NETWORK_PEERS_TO_FETCH = 100;

////////////////////////////
/// Cache / Save ///
////////////////////////////

export const BLOCKS_TO_SAVE_NETWORK_PEERS = 100;
export const BLOCKS_TO_CACHE_TOKEN_SUMMARY = 100;
export const BLOCKS_TO_CHECK_BANNED_VALIDATOR = 75;
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { ICommandHandler, CommandHandler } from '@nestjs/cqrs';
import { LokiLogger } from 'nestjs-loki-logger';
import { NodeApiService } from 'src/modules/node-api/node-api.service';
import { PrismaService } from 'src/modules/prisma/prisma.service';

export class CheckForBlockFinalityCommand {}

@CommandHandler(CheckForBlockFinalityCommand)
export class CheckForBlockFinalityHandler implements ICommandHandler<CheckForBlockFinalityCommand> {
private logger = new LokiLogger(CheckForBlockFinalityHandler.name);

constructor(
private readonly prisma: PrismaService,
private readonly nodeApi: NodeApiService,
) {}

async execute() {
this.logger.debug('Checking for block finality');
await this.nodeApi.getAndSetNodeInfo();
await this.prisma.block.updateMany({
where: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { LokiLogger } from 'nestjs-loki-logger';
import { BLOCKS_TO_CHECK_BANNED_VALIDATOR } from 'src/config/constants';
import { NodeApi, NodeApiService } from 'src/modules/node-api/node-api.service';
import { AllValidators } from 'src/modules/node-api/types';
import { PrismaService } from 'src/modules/prisma/prisma.service';
import { ValidatorStatus } from '../../event/types';

export class CheckValidatorStatusCommand {
constructor(public readonly blockHeight: number) {}
}

@CommandHandler(CheckValidatorStatusCommand)
export class CheckValidatorStatusHandler implements ICommandHandler<CheckValidatorStatusCommand> {
private readonly logger = new LokiLogger(CheckValidatorStatusHandler.name);

constructor(
private readonly prisma: PrismaService,
private readonly nodeApi: NodeApiService,
) {}

async execute({ blockHeight }: CheckValidatorStatusCommand) {
if (blockHeight % BLOCKS_TO_CHECK_BANNED_VALIDATOR !== 0) return;
this.logger.debug('Checking validator status');
const validators = await this.getAllValidatorsFromNode();
if (!validators) return;

await this.updateBannedValidators(validators);

this.logger.debug('Validator banned status updated successfully');
}

async getAllValidatorsFromNode(): Promise<AllValidators> {
try {
const validators = await this.nodeApi.invokeApi<AllValidators>(
NodeApi.POS_GET_ALL_VALIDATORS,
{},
);
return validators;
} catch (error) {
this.logger.error('Error: Failed to fetch all validators', error);
}
}

async updateBannedValidators({ validators }: AllValidators): Promise<void> {
for (const validator of validators) {
if (validator.isBanned) {
await this.prisma.validator.update({
where: { address: validator.address },
data: {
isBanned: true,
status: ValidatorStatus.BANNED,
totalStake: BigInt(validator.totalStake),
selfStake: BigInt(validator.selfStake),
lastGeneratedHeight: validator.lastGeneratedHeight,
reportMisbehaviorHeights: JSON.stringify(validator.reportMisbehaviorHeights),
consecutiveMissedBlocks: validator.consecutiveMissedBlocks,
commission: validator.commission,
lastCommissionIncreaseHeight: validator.lastCommissionIncreaseHeight,
sharingCoefficients: JSON.stringify(validator.sharingCoefficients),
punishmentPeriods: JSON.stringify(validator.punishmentPeriods),
},
});
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ICommandHandler, CommandHandler } from '@nestjs/cqrs';
import { LokiLogger } from 'nestjs-loki-logger';
import { PrismaService } from 'src/modules/prisma/prisma.service';

export class UpdateBlocksFeeCommand {
Expand All @@ -12,9 +13,12 @@ export interface UpdateBlockFee {

@CommandHandler(UpdateBlocksFeeCommand)
export class UpdateBlocksFeeHandler implements ICommandHandler<UpdateBlocksFeeCommand> {
private logger = new LokiLogger(UpdateBlocksFeeHandler.name);

constructor(private readonly prisma: PrismaService) {}

async execute(command: UpdateBlocksFeeCommand): Promise<void> {
this.logger.debug('Updating blocks fee');
if (!command.payload) return;

for (const [height, { totalBurnt, totalFee }] of command.payload.entries()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ type ValidatorRewardMap = {

@CommandHandler(UpdateBlockGeneratorCommand)
export class UpdateBlockGeneratorHandler implements ICommandHandler<UpdateBlockGeneratorCommand> {
private logger = new LokiLogger(UpdateBlockGeneratorHandler.name);
constructor(private readonly prisma: PrismaService) {}

async execute(command: UpdateBlockGeneratorCommand) {
this.logger.debug('Updating block generator rewards');
const validatorData = new Map<string, ValidatorRewardMap>();
const chainEventsMap = new Map<number, ChainEvent[]>();

Expand Down
2 changes: 2 additions & 0 deletions src/modules/indexer/indexer.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { UpdateValidatorRanksHandler } from './event/commands/update-validator-r
import { UpdateAccountHandler } from './event/commands/update-account.command';
import { UpdateSidechainHandler } from './event/commands/update-sidechain.command';
import { SaveNetworkPeersHandler } from './block/post-block-commands/save-network-peers.command';
import { CheckValidatorStatusHandler } from './block/post-block-commands/check-validator-status.command';

@Module({
imports: [CqrsModule, StateModule, NodeApiModule],
Expand All @@ -41,6 +42,7 @@ import { SaveNetworkPeersHandler } from './block/post-block-commands/save-networ
UpdateBlockGeneratorHandler,
UpdateBlocksFeeHandler,
SaveNetworkPeersHandler,
CheckValidatorStatusHandler,

// chain events commands
ExecutionResultHandler,
Expand Down
2 changes: 2 additions & 0 deletions src/modules/indexer/indexer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { OnEvent } from '@nestjs/event-emitter';
import * as fs from 'fs';
import * as path from 'path';
import { SaveNetworkPeersCommand } from './block/post-block-commands/save-network-peers.command';
import { CheckValidatorStatusCommand } from './block/post-block-commands/check-validator-status.command';

// Sets `genesisHeight` as `nextBlockToSync`
// `SYNCING`: Will send new block events to queue from `nextBlockToSync` to current `nodeHeight`
Expand Down Expand Up @@ -92,6 +93,7 @@ export class IndexerService {

await Promise.all([
this.commandBus.execute(new SaveNetworkPeersCommand(blocks.at(0).header.height)),
this.commandBus.execute(new CheckValidatorStatusCommand(blocks.at(0).header.height)),
this.commandBus.execute(new CheckForBlockFinalityCommand()),
]);

Expand Down
134 changes: 134 additions & 0 deletions test/commands/check-validator-status.handler.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Test, TestingModule } from '@nestjs/testing';
import { PrismaService } from 'src/modules/prisma/prisma.service';
import { NodeApi, NodeApiService } from 'src/modules/node-api/node-api.service';
import { LokiLogger } from 'nestjs-loki-logger';
import { CheckValidatorStatusHandler } from 'src/modules/indexer/block/post-block-commands/check-validator-status.command';
import { ValidatorStatus } from 'src/modules/pos/types';
import { AllValidators } from 'src/modules/node-api/types';
import { BLOCKS_TO_CHECK_BANNED_VALIDATOR } from 'src/config/constants';

describe('CheckValidatorStatusHandler', () => {
let handler: CheckValidatorStatusHandler;
let prismaService: PrismaService;
let nodeApiService: NodeApiService;

beforeEach(async () => {
const mockValidators: AllValidators = {
validators: [
{
name: 'przemer',
totalStake: '103150000000000',
selfStake: '9899000000000',
lastGeneratedHeight: 397037,
isBanned: true,
reportMisbehaviorHeights: [],
consecutiveMissedBlocks: 2209,
commission: 9500,
lastCommissionIncreaseHeight: 68231,
sharingCoefficients: [
{
tokenID: '0200000000000000',
coefficient: '20c157e32fecfda501718a0f',
},
],
address: 'klyhf3gtj3wecxaxy7csqb5k8kszugghpscvat5b4',
punishmentPeriods: [],
},
{
name: 'validator2',
totalStake: '50000000000000',
selfStake: '5000000000000',
lastGeneratedHeight: 300000,
isBanned: false,
reportMisbehaviorHeights: [1, 2, 3],
consecutiveMissedBlocks: 1000,
commission: 5000,
lastCommissionIncreaseHeight: 50000,
sharingCoefficients: [
{
tokenID: '0100000000000000',
coefficient: '10c157e32fecfda501718a0f',
},
],
address: 'klyhf3gtj3wecxaxy7csqb5k8kszugghpscvat5b5',
punishmentPeriods: [],
},
],
};

const module: TestingModule = await Test.createTestingModule({
providers: [
CheckValidatorStatusHandler,
{
provide: PrismaService,
useValue: {
validator: {
update: jest.fn(),
},
},
},
{
provide: NodeApiService,
useValue: {
invokeApi: jest.fn().mockResolvedValue(mockValidators),
},
},
{
provide: LokiLogger,
useValue: {
debug: jest.fn(),
error: jest.fn(),
},
},
],
}).compile();

handler = module.get<CheckValidatorStatusHandler>(CheckValidatorStatusHandler);
prismaService = module.get<PrismaService>(PrismaService);
nodeApiService = module.get<NodeApiService>(NodeApiService);
});

it('should return early if blockHeight is not a multiple of BLOCKS_TO_CHECK_BANNED_VALIDATOR', async () => {
await handler.execute({ blockHeight: BLOCKS_TO_CHECK_BANNED_VALIDATOR + 1 });

expect(nodeApiService.invokeApi).not.toHaveBeenCalled();
expect(prismaService.validator.update).not.toHaveBeenCalled();
});

it('should fetch and update banned validators', async () => {
await handler.execute({ blockHeight: BLOCKS_TO_CHECK_BANNED_VALIDATOR });

expect(nodeApiService.invokeApi).toHaveBeenCalledWith(NodeApi.POS_GET_ALL_VALIDATORS, {});
expect(prismaService.validator.update).toHaveBeenCalledWith({
where: { address: 'klyhf3gtj3wecxaxy7csqb5k8kszugghpscvat5b4' },
data: {
isBanned: true,
status: ValidatorStatus.BANNED,
totalStake: BigInt('103150000000000'),
selfStake: BigInt('9899000000000'),
lastGeneratedHeight: 397037,
reportMisbehaviorHeights: JSON.stringify([]),
consecutiveMissedBlocks: 2209,
commission: 9500,
lastCommissionIncreaseHeight: 68231,
sharingCoefficients: JSON.stringify([
{
tokenID: '0200000000000000',
coefficient: '20c157e32fecfda501718a0f',
},
]),
punishmentPeriods: JSON.stringify([]),
},
});
expect(prismaService.validator.update).toHaveBeenCalledTimes(1);
});

it('should handle errors when fetching validators', async () => {
jest.spyOn(nodeApiService, 'invokeApi').mockRejectedValue(new Error('API Error'));

await handler.execute({ blockHeight: BLOCKS_TO_CHECK_BANNED_VALIDATOR });

expect(nodeApiService.invokeApi).toHaveBeenCalledWith(NodeApi.POS_GET_ALL_VALIDATORS, {});
expect(prismaService.validator.update).not.toHaveBeenCalled();
});
});
61 changes: 0 additions & 61 deletions test/indexer/indexer.service.spec.ts

This file was deleted.

0 comments on commit d71026c

Please sign in to comment.