From d474564752af9a28ec9503eef0b828bc1fcb1db9 Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Tue, 28 Nov 2023 13:08:37 -0300 Subject: [PATCH] add limit for slot range and pools --- .../DelegationForPoolController.ts | 80 ++++++++++++------- webserver/shared/constants.ts | 5 ++ webserver/shared/errors.ts | 14 ++++ 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/webserver/server/app/controllers/DelegationForPoolController.ts b/webserver/server/app/controllers/DelegationForPoolController.ts index a2e6b8a5..696fb6b4 100644 --- a/webserver/server/app/controllers/DelegationForPoolController.ts +++ b/webserver/server/app/controllers/DelegationForPoolController.ts @@ -2,44 +2,66 @@ import { Body, Controller, TsoaResponse, Res, Post, Route, SuccessResponse } fro import { StatusCodes } from 'http-status-codes'; import tx from 'pg-tx'; import pool from '../services/PgPoolSingleton'; -import type { ErrorShape } from '../../../shared/errors'; +import { genErrorMessage, type ErrorShape, Errors } from '../../../shared/errors'; import type { EndpointTypes } from '../../../shared/routes'; import { Routes } from '../../../shared/routes'; import { delegationsForPool } from '../services/DelegationForPool'; import { DelegationForPoolResponse } from '../../../shared/models/DelegationForPool'; +import { POOL_DELEGATION_LIMIT } from '../../../shared/constants'; const route = Routes.delegationForPool; @Route('delegation/pool') export class DelegationForPoolController extends Controller { - @SuccessResponse(`${StatusCodes.OK}`) - @Post() - public async delegationForPool( - @Body() - requestBody: EndpointTypes[typeof route]['input'], - @Res() - errorResponse: TsoaResponse< - StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY, - ErrorShape - > - ): Promise { - const response = await tx< - DelegationForPoolResponse - >(pool, async dbTx => { - const data = await delegationsForPool({ - pools: requestBody.pools.map(poolId => Buffer.from(poolId, 'hex')), - range: requestBody.range, - dbTx - }); - - return data.map(data => ({ - credential: data.credential as string, - pool: data.pool, - txId: data.tx_id as string, - slot: data.slot, - })); - }); + @SuccessResponse(`${StatusCodes.OK}`) + @Post() + public async delegationForPool( + @Body() + requestBody: EndpointTypes[typeof route]['input'], + @Res() + errorResponse: TsoaResponse< + StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY, + ErrorShape + > + ): Promise { + if (requestBody.pools.length > POOL_DELEGATION_LIMIT.POOLS) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.PoolsLimitExceeded, { + limit: POOL_DELEGATION_LIMIT.POOLS, + found: requestBody.pools.length, + }) + ); + } - return response; + const slotRangeSize = requestBody.range.maxSlot - requestBody.range.minSlot; + if (slotRangeSize > POOL_DELEGATION_LIMIT.SLOT_RANGE) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.SlotRangeLimitExceeded, { + limit: POOL_DELEGATION_LIMIT.SLOT_RANGE, + found: slotRangeSize, + }) + ); } + + const response = await tx(pool, async dbTx => { + const data = await delegationsForPool({ + pools: requestBody.pools.map(poolId => Buffer.from(poolId, 'hex')), + range: requestBody.range, + dbTx, + }); + + return data.map(data => ({ + credential: data.credential as string, + pool: data.pool, + txId: data.tx_id as string, + slot: data.slot, + })); + }); + + return response; + } } diff --git a/webserver/shared/constants.ts b/webserver/shared/constants.ts index dcc104d3..f5c7c900 100644 --- a/webserver/shared/constants.ts +++ b/webserver/shared/constants.ts @@ -26,3 +26,8 @@ export const DEX_PRICE_LIMIT = { REQUEST_ASSET_PAIRS: 100, RESPONSE: 1000, }; + +export const POOL_DELEGATION_LIMIT = { + POOLS: 50, + SLOT_RANGE: 200, +}; diff --git a/webserver/shared/errors.ts b/webserver/shared/errors.ts index 8953a6cf..d37c09c4 100644 --- a/webserver/shared/errors.ts +++ b/webserver/shared/errors.ts @@ -12,6 +12,8 @@ export enum ErrorCodes { AssetLimitExceeded = 8, CredentialLimitExceeded = 9, AssetPairLimitExceeded = 10, + PoolsLimitExceeded = 11, + SlotRangeLimitExceeded = 12, } export type ErrorShape = { @@ -86,6 +88,18 @@ export const Errors = { detailsGen: (details: { limit: number; found: number }) => `Limit of ${details.limit}, found ${details.found}`, }, + PoolsLimitExceeded: { + code: ErrorCodes.PoolsLimitExceeded, + prefix: "Exceeded request pools limit.", + detailsGen: (details: { limit: number; found: number }) => + `Limit of ${details.limit}, found ${details.found}`, + }, + SlotRangeLimitExceeded: { + code: ErrorCodes.SlotRangeLimitExceeded, + prefix: "Exceeded request slot range limit.", + detailsGen: (details: { limit: number; found: number }) => + `Limit of ${details.limit}, found ${details.found}`, + }, } as const; export function genErrorMessage(