Skip to content

Commit

Permalink
add getNodesWithAuctionDangerZoneFilter
Browse files Browse the repository at this point in the history
  • Loading branch information
cfaur09 committed Feb 6, 2024
1 parent 5d376d2 commit 1bc7c00
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/endpoints/nodes/entities/node.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ export class NodeFilter {

@Field(() => Boolean, { description: "Node isQualified filter for the given nodes.", nullable: true })
isQualified: boolean | undefined;

@Field(() => Boolean, { description: "Node isAuctionDangeZone filter for the given nodes.", nullable: true })
isAuctionDangerZone: boolean | undefined;
}
4 changes: 3 additions & 1 deletion src/endpoints/nodes/node.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class NodeController {
@ApiQuery({ name: 'sort', description: 'Sorting criteria', required: false, enum: SortNodes })
@ApiQuery({ name: 'order', description: 'Sorting order (asc / desc)', required: false, enum: SortOrder })
@ApiQuery({ name: 'isQualified', description: 'Whether node is qualified or not', required: false, type: 'boolean' })
@ApiQuery({ name: 'isAuctionDangerZone', description: 'Whether node is in danger zone or not', required: false, type: 'boolean' })
async getNodes(
@Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number,
@Query('size', new DefaultValuePipe(25), ParseIntPipe) size: number,
Expand All @@ -54,8 +55,9 @@ export class NodeController {
@Query('sort', new ParseEnumPipe(NodeSort)) sort?: NodeSort,
@Query('order', new ParseEnumPipe(SortOrder)) order?: SortOrder,
@Query('isQualified', ParseBoolPipe) isQualified?: boolean,
@Query('isAuctionDangerZone', ParseBoolPipe) isAuctionDangerZone?: boolean,
): Promise<Node[]> {
return await this.nodeService.getNodes(new QueryPagination({ from, size }), new NodeFilter({ search, keys, online, type, status, shard, issues, identity, provider, owner, auctioned, fullHistory, sort, order, isQualified }));
return await this.nodeService.getNodes(new QueryPagination({ from, size }), new NodeFilter({ search, keys, online, type, status, shard, issues, identity, provider, owner, auctioned, fullHistory, sort, order, isQualified, isAuctionDangerZone }));
}

@Get("/nodes/versions")
Expand Down
15 changes: 15 additions & 0 deletions src/endpoints/nodes/node.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ export class NodeService {
private async getFilteredNodes(query: NodeFilter): Promise<Node[]> {
const allNodes = await this.getAllNodes();

if (query.isAuctionDangerZone) {
return this.getNodesWithAuctionDangerZoneFilter();
}

const filteredNodes = allNodes.filter(node => {
if (query.search !== undefined) {
const nodeMatches = node.bls && node.bls.toLowerCase().includes(query.search.toLowerCase());
Expand Down Expand Up @@ -668,4 +672,15 @@ export class NodeService {

return keys;
}

async getNodesWithAuctionDangerZoneFilter(): Promise<Node[]> {
const nodes = await this.getAllNodes();
const minimumAuctionStake = await this.stakeService.getMinimumAuctionStake();
const dangerZoneThreshold = BigInt(minimumAuctionStake) * BigInt(105) / BigInt(100);

return nodes.filter(node =>
node.status === 'eligible' &&
BigInt(node.stake) < dangerZoneThreshold
);
}
}
38 changes: 38 additions & 0 deletions src/test/unit/services/nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ describe('NodeService', () => {
provide: StakeService,
useValue: {
getStakes: jest.fn(),
getMinimumAuctionStake: jest.fn(),
},
},
{
Expand Down Expand Up @@ -449,4 +450,41 @@ describe('NodeService', () => {
});
});
});

describe('getNodesWithAuctionDangerZoneFilter', () => {
const mockNodes = JSON.parse(fs.readFileSync(path.join(__dirname, '../../mocks/nodes.mock.json'), 'utf-8'));
const minimumAuctionStake = '2625000000000000000000'; // 2500 + 5%

beforeEach(() => {
jest.restoreAllMocks();
});

it('should return nodes in danger zone', async () => {
jest.spyOn(nodeService['stakeService'], 'getMinimumAuctionStake').mockResolvedValue(minimumAuctionStake);

jest.spyOn(nodeService, 'getAllNodes').mockResolvedValue(mockNodes);

const result = await nodeService.getNodesWithAuctionDangerZoneFilter();
expect(result.length).toBeGreaterThan(0);

for (const node of result) {
expect(BigInt(node.stake)).toBeLessThan(BigInt(minimumAuctionStake));
expect(node.status).toEqual('eligible');
}
});

it('should return an empty array if no nodes are in danger zone', async () => {
const modifiedNodes = mockNodes.map((node: any) => ({
...node,
stake: '2800000000000000000000',
}));

jest.spyOn(nodeService['stakeService'], 'getMinimumAuctionStake').mockResolvedValue(minimumAuctionStake);
jest.spyOn(nodeService, 'getAllNodes').mockResolvedValue(modifiedNodes);

const result = await nodeService.getNodesWithAuctionDangerZoneFilter();

expect(result.length).toBe(0);
});
});
});

0 comments on commit 1bc7c00

Please sign in to comment.