Skip to content

Commit

Permalink
add support for account keys status filter (#1139)
Browse files Browse the repository at this point in the history
* add support for account keys status filter

* add sortNodesByStatus function

* update specs

* small fix

* simpler sort function

---------

Co-authored-by: tanghel <[email protected]>
  • Loading branch information
cfaur09 and tanghel authored Nov 23, 2023
1 parent d5ae8fa commit 828258b
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 6 deletions.
17 changes: 15 additions & 2 deletions src/endpoints/accounts/account.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import { AccountFilter } from './entities/account.filter';
import { AccountSort } from './entities/account.sort';
import { AccountHistoryFilter } from './entities/account.history.filter';
import { ParseArrayPipeOptions } from '@multiversx/sdk-nestjs-common/lib/pipes/entities/parse.array.options';
import { NodeStatusRaw } from '../nodes/entities/node.status';
import { AccountKeyFilter } from './entities/account.key.filter';

@Controller()
@ApiTags('accounts')
Expand Down Expand Up @@ -619,8 +621,19 @@ export class AccountController {
@Get("/accounts/:address/keys")
@ApiOperation({ summary: 'Account nodes', description: 'Returns all active / queued nodes where the account is owner' })
@ApiOkResponse({ type: [AccountKey] })
async getAccountKeys(@Param('address', ParseAddressPipe) address: string): Promise<AccountKey[]> {
return await this.accountService.getKeys(address);
@ApiQuery({ name: 'from', description: 'Number of items to skip for the result set', required: false })
@ApiQuery({ name: 'size', description: 'Number of items to retrieve', required: false })
@ApiQuery({ name: 'status', description: 'Key status', required: false, enum: NodeStatusRaw })
async getAccountKeys(
@Param('address', ParseAddressPipe) address: string,
@Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number,
@Query('size', new DefaultValuePipe(25), ParseIntPipe) size: number,
@Query('status', new ParseEnumArrayPipe(NodeStatusRaw)) status?: NodeStatusRaw[],
): Promise<AccountKey[]> {
return await this.accountService.getKeys(
address,
new AccountKeyFilter({ status }),
new QueryPagination({ from, size }));
}

@Get("/accounts/:address/waiting-list")
Expand Down
20 changes: 18 additions & 2 deletions src/endpoints/accounts/account.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { ProviderService } from '../providers/provider.service';
import { Provider } from '../providers/entities/provider';
import { KeysService } from '../keys/keys.service';
import { NodeStatusRaw } from '../nodes/entities/node.status';
import { AccountKeyFilter } from './entities/account.key.filter';

@Injectable()
export class AccountService {
Expand Down Expand Up @@ -429,7 +430,8 @@ export class AccountService {
);
}

async getKeys(address: string): Promise<AccountKey[]> {
async getKeys(address: string, filter: AccountKeyFilter, pagination: QueryPagination): Promise<AccountKey[]> {
const { from, size } = pagination;
const publicKey = AddressUtils.bech32Decode(address);
const isStakingProvider = await this.providerService.isProvider(address);

Expand All @@ -451,7 +453,14 @@ export class AccountService {
await this.updateQueuedNodes(nodes);
}

return [...notStakedNodes, ...nodes];
let filteredNodes = [...notStakedNodes, ...nodes];

if (filter && filter.status && filter.status.length > 0) {
filteredNodes = filteredNodes.filter(node => filter.status.includes(node.status as NodeStatusRaw));
filteredNodes = this.sortNodesByStatus(filteredNodes, filter.status);
}

return filteredNodes.slice(from, from + size);
}

getInactiveNodesBuffers(allNodeStates: string[]): string[] {
Expand Down Expand Up @@ -519,6 +528,13 @@ export class AccountService {
return nodes;
}

private sortNodesByStatus(nodes: AccountKey[], status: NodeStatusRaw[]): AccountKey[] {
return nodes.sorted(node => {
const statusIndex = status.indexOf(node.status as NodeStatusRaw);
return statusIndex === -1 ? status.length : statusIndex;
});
}

async applyRewardAddressAndTopUpToNodes(nodes: AccountKey[], address: string) {
if (nodes.length) {
const rewardAddress = await this.getRewardAddressForNode(nodes[0].blsKey);
Expand Down
13 changes: 13 additions & 0 deletions src/endpoints/accounts/entities/account.key.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import { Field, ObjectType } from "@nestjs/graphql";
import { NodeStatusRaw } from "src/endpoints/nodes/entities/node.status";

@ObjectType("AccountKeyFilter", { description: "Account key filter object type." })
export class AccountKeyFilter {
constructor(init?: Partial<AccountKeyFilter>) {
Object.assign(this, init);
}

@Field(() => AccountKeyFilter, { description: "Account key status filter for the given keys.", nullable: true })
status: NodeStatusRaw[] = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { SmartContractResultService } from "src/endpoints/sc-results/scresult.se
import { AccountHistory } from "src/endpoints/accounts/entities/account.history";
import { AccountEsdtHistory } from "src/endpoints/accounts/entities/account.esdt.history";
import { AccountHistoryFilter } from "src/endpoints/accounts/entities/account.history.filter";
import { AccountKeyFilter } from "src/endpoints/accounts/entities/account.key.filter";

@Resolver(() => AccountDetailed)
export class AccountDetailedResolver extends AccountDetailedQuery {
Expand Down Expand Up @@ -78,7 +79,7 @@ export class AccountDetailedResolver extends AccountDetailedQuery {

@ResolveField("keys", () => [AccountKey], { name: "keys", description: "Returns all nodes in the node queue where the account is owner." })
public async getKeys(@Parent() account: AccountDetailed) {
return await this.accountService.getKeys(account.address);
return await this.accountService.getKeys(account.address, new AccountKeyFilter(), new QueryPagination());
}

@ResolveField("resultsAccount", () => [SmartContractResult], { name: "resultsAccount", description: "Returns smart contract results where the account is sender or receiver." })
Expand Down
1 change: 0 additions & 1 deletion src/test/unit/services/nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ describe('NodeService', () => {
it('should return an array of BLS keys', async () => {
const address = 'erd1kz2kumr0clug4ht2ek0l4l9drvq3rne9lmkwrjf3qv2luyuuaj2szjwv0f';
const expectedBls = ["00198be6aae517a382944cd5a97845857f3b122bb1edf1588d60ed421d32d16ea2767f359a0d714fae3a35c1b0cf4e1141d701d5d1d24160e49eeaebeab21e2f89a2b7c44f3a313383d542e69800cfb6e115406d3d8114b4044ef5a04acf0408"];

const getBlsKeysStatusListEncoded = [
'ABmL5qrlF6OClEzVqXhFhX87Eiux7fFYjWDtQh0y0W6idn81mg1xT646NcGwz04RQdcB1dHSQWDknurr6rIeL4mit8RPOjEzg9VC5pgAz7bhFUBtPYEUtARO9aBKzwQI',
'ADumI38PfCae6/7LagoHlgdsAlk4RuHOia7puDK5TdVOk9NbA9w9WUSxqukWciUG+vlZpHyr8tAPVnrVCxD48aQKsDFv3zAkVPeupYsjEJzP3OCCvRb7JiNCoTgrgCwQ',
Expand Down

0 comments on commit 828258b

Please sign in to comment.