Skip to content

Commit

Permalink
add epochsLeft key (#1155)
Browse files Browse the repository at this point in the history
* add epochsLeft key

* fix test

* apply epochsLeft only for nodes with Status = Waiting
update specs

* moved waiting epochs left inside node check

* add epochLeft under feature flag

* add isNodeEpochsLeft spec

---------

Co-authored-by: tanghel <[email protected]>
  • Loading branch information
cfaur09 and tanghel authored Mar 13, 2024
1 parent 8ea02d1 commit 9018295
Show file tree
Hide file tree
Showing 16 changed files with 112 additions and 10 deletions.
2 changes: 2 additions & 0 deletions config/config.devnet-old.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ features:
admins:
- ''
jwtSecret: ''
nodeEpochsLeft:
enabled: false
image:
width: 600
height: 600
Expand Down
2 changes: 2 additions & 0 deletions config/config.devnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ features:
admins:
- ''
jwtSecret: ''
nodeEpochsLeft:
enabled: false
transactionProcessor:
enabled: false
maxLookBehind: 100
Expand Down
2 changes: 2 additions & 0 deletions config/config.mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ features:
admins:
- ''
jwtSecret: ''
nodeEpochsLeft:
enabled: false
transactionProcessor:
enabled: false
maxLookBehind: 1000
Expand Down
2 changes: 2 additions & 0 deletions config/config.testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ features:
admins:
- ''
jwtSecret: ''
nodeEpochsLeft:
enabled: false
transactionProcessor:
enabled: false
maxLookBehind: 100
Expand Down
4 changes: 4 additions & 0 deletions src/common/api-config/api.config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,10 @@ export class ApiConfigService {
return this.configService.get<number>('features.nftExtendedAttributes.nsfwThreshold') ?? 0.85;
}

isNodeEpochsLeftEnabled(): boolean {
return this.configService.get<boolean>('features.nodeEpochsLeft.enabled') ?? false;
}

getIndexerSlaveConnections(): DatabaseConnectionOptions[] {
const slaves = this.configService.get<DatabaseConnectionOptions[]>('indexer.slaves');
if (!slaves) {
Expand Down
1 change: 1 addition & 0 deletions src/common/gateway/entities/gateway.component.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export enum GatewayComponentRequest {
addressEsdtBalance = 'addressEsdtBalance',
addressNfts = 'addressNfts',
nodeHeartbeat = 'nodeHeartbeat',
getNodeWaitingEpochsLeft = 'getNodeWaitingEpochsLeft',
validatorStatistics = 'validatorStatistics',
transactionDetails = 'transactionDetails',
sendTransaction = 'sendTransaction',
Expand Down
5 changes: 5 additions & 0 deletions src/common/gateway/gateway.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export class GatewayService {
return result;
}

async getNodeWaitingEpochsLeft(bls: string): Promise<number> {
const result = await this.get(`node/waiting-epochs-left/${bls}`, GatewayComponentRequest.getNodeWaitingEpochsLeft);
return result.epochsLeft;
}

async getTransactionProcessStatus(txHash: string): Promise<TransactionProcessStatus> {
// eslint-disable-next-line require-await
const result = await this.get(`transaction/${txHash}/process-status`, GatewayComponentRequest.transactionProcessStatus, async (error) => {
Expand Down
4 changes: 4 additions & 0 deletions src/endpoints/nodes/entities/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,8 @@ export class Node {
@Field(() => Number, { description: "Remaining UnBond Period for node with status leaving.", nullable: true })
@ApiProperty({ type: Number, example: 10 })
remainingUnBondPeriod: number | undefined = undefined;

@Field(() => Number, { description: "Number of epochs left for a node in waiting state.", nullable: true })
@ApiProperty({ type: Number, example: 15 })
epochsLeft: number | undefined = undefined;
}
9 changes: 8 additions & 1 deletion src/endpoints/nodes/node.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ export class NodeService {

async getNode(bls: string): Promise<Node | undefined> {
const allNodes = await this.getAllNodes();
return allNodes.find(x => x.bls === bls);
const node = allNodes.find(x => x.bls === bls);

if (this.apiConfigService.isNodeEpochsLeftEnabled()) {
if (node && node.status === NodeStatus.waiting) {
node.epochsLeft = await this.gatewayService.getNodeWaitingEpochsLeft(bls);
}
}
return node;
}

async getNodeCount(query: NodeFilter): Promise<number> {
Expand Down
3 changes: 3 additions & 0 deletions src/graphql/schema/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -2747,6 +2747,9 @@ type Node {
"""Bls address for the given node."""
bls: String!

"""Number of epochs left for a node in waiting state."""
epochsLeft: Float

"""Full history details for the given node."""
fullHistory: Boolean

Expand Down
26 changes: 26 additions & 0 deletions src/test/mocks/nodes.mock.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@
"validatorSuccess": 1892,
"position": 0
},
{
"bls": "72043abcbb6c3f472c3f486eb181794eeb11c70df5cdbb2c12eb16b92ec0dea12dfae0762641d92bf9a1c8743156f008f19a6a1b5160ace7c304390133c47a54399def909a7caeb59b9534002e7de7140d3241cce1a857f6c733dcfa80a1f28e",
"name": "TheNode",
"version": "v1.2.38.0",
"identity": "thenode",
"rating": 100,
"tempRating": 100,
"ratingModifier": 1.2,
"shard": 1,
"type": "validator",
"status": "waiting",
"online": true,
"nonce": 8160575,
"instances": 1,
"owner": "erd1kz2kumr0clug4ht2ek0l4l9drvq3rne9lmkwrjf3qv2luyuuaj2szjwv0f",
"provider": "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqy8lllls62y8s5",
"stake": "2500000000000000000000",
"topUp": "688187259066013399629",
"locked": "3188187259066013399629",
"leaderFailure": 0,
"leaderSuccess": 28,
"validatorFailure": 0,
"validatorIgnoredSignatures": 0,
"validatorSuccess": 1892,
"position": 0
},
{
"bls": "003ba6237f0f7c269eebfecb6a0a0796076c02593846e1ce89aee9b832b94dd54e93d35b03dc3d5944b1aae916722506faf959a47cabf2d00f567ad50b10f8f1a40ab0316fdf302454f7aea58b23109ccfdce082bd16fb262342a1382b802c10",
"name": "Raven2",
Expand Down
19 changes: 19 additions & 0 deletions src/test/unit/services/api.config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1676,4 +1676,23 @@ describe('API Config', () => {
expect(() => apiConfigService.getSelfUrl()).toThrowError('No self url present');
});
});

describe('isNodeEpochsLeftEnabled', () => {
it("should return Node Epochs Left active flag", () => {
jest
.spyOn(ConfigService.prototype, "get")
.mockImplementation(jest.fn(() => true));

const results = apiConfigService.isNodeEpochsLeftEnabled();
expect(results).toEqual(true);
});

it("should throw error because test simulates that node epochs left flag is not defined", () => {
jest
.spyOn(ConfigService.prototype, 'get')
.mockImplementation(jest.fn(() => undefined));

expect(apiConfigService.isNodeEpochsLeftEnabled()).toStrictEqual(false);
});
});
});
1 change: 1 addition & 0 deletions src/test/unit/services/delegation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ describe('Delegation Service', () => {
issues: [],
syncProgress: undefined,
remainingUnBondPeriod: undefined,
epochsLeft: undefined,
},
];
});
Expand Down
36 changes: 30 additions & 6 deletions src/test/unit/services/nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('NodeService', () => {
let cacheService: CacheService;
let vmQueryService: VmQueryService;
let apiConfigService: ApiConfigService;
let gatewayService: GatewayService;

beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
Expand All @@ -36,6 +37,7 @@ describe('NodeService', () => {
getNetworkConfig: jest.fn(),
getValidatorAuctions: jest.fn(),
getNodeHeartbeatStatus: jest.fn(),
getNodeWaitingEpochsLeft: jest.fn(),
},
},
{
Expand All @@ -52,6 +54,7 @@ describe('NodeService', () => {
getStakingContractAddress: jest.fn(),
getAuctionContractAddress: jest.fn(),
isNodeSyncProgressEnabled: jest.fn(),
isNodeEpochsLeftEnabled: jest.fn(),
},
},
{
Expand Down Expand Up @@ -102,6 +105,7 @@ describe('NodeService', () => {
cacheService = moduleRef.get<CacheService>(CacheService);
vmQueryService = moduleRef.get<VmQueryService>(VmQueryService);
apiConfigService = moduleRef.get<ApiConfigService>(ApiConfigService);
gatewayService = moduleRef.get<GatewayService>(GatewayService);
});

beforeEach(() => { jest.restoreAllMocks(); });
Expand Down Expand Up @@ -190,14 +194,34 @@ describe('NodeService', () => {
const result = await nodeService.getNode(bls);
expect(result).toStrictEqual(expectedNode);
});

it('should return epochsLeft key from gateway for a specific node', async () => {
const bls = "72043abcbb6c3f472c3f486eb181794eeb11c70df5cdbb2c12eb16b92ec0dea12dfae0762641d92bf9a1c8743156f008f19a6a1b5160ace7c304390133c47a54399def909a7caeb59b9534002e7de7140d3241cce1a857f6c733dcfa80a1f28e";

jest.spyOn(apiConfigService, 'isNodeEpochsLeftEnabled').mockReturnValue(true);
// eslint-disable-next-line require-await
jest.spyOn(nodeService['cacheService'], 'getOrSet').mockImplementation(async (key, getter) => {
if (key === CacheInfo.Nodes.key) {
return mockNodes;
}
return getter();
});

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

const result = await nodeService.getNode(bls);

expect(result).toEqual(expect.objectContaining({ epochsLeft: 10 }));
});
});

describe('getNodeCount', () => {
it('returns the correct count of nodes without NodeFilter applied', async () => {
const allNodesSpy = jest.spyOn(nodeService, 'getAllNodes').mockResolvedValueOnce(Promise.resolve(mockNodes));
const result = await nodeService.getNodeCount(new NodeFilter());

expect(result).toStrictEqual(99);
expect(result).toStrictEqual(100);
expect(allNodesSpy).toHaveBeenCalledTimes(1);
});

Expand All @@ -213,15 +237,15 @@ describe('NodeService', () => {
const allNodesSpy = jest.spyOn(nodeService, 'getAllNodes').mockResolvedValueOnce(Promise.resolve(mockNodes));
const result = await nodeService.getNodeCount(new NodeFilter({ type: NodeType.validator }));

expect(result).toStrictEqual(97);
expect(result).toStrictEqual(98);
expect(allNodesSpy).toHaveBeenCalledTimes(1);
});

it('should return the correct count of nodes with status online', async () => {
const allNodesSpy = jest.spyOn(nodeService, 'getAllNodes').mockResolvedValueOnce(Promise.resolve(mockNodes));
const result = await nodeService.getNodeCount(new NodeFilter({ online: true }));

expect(result).toStrictEqual(97);
expect(result).toStrictEqual(98);
expect(allNodesSpy).toHaveBeenCalledTimes(1);
});

Expand Down Expand Up @@ -254,7 +278,7 @@ describe('NodeService', () => {
const owner = "erd1kz2kumr0clug4ht2ek0l4l9drvq3rne9lmkwrjf3qv2luyuuaj2szjwv0f";
const result = await nodeService.getNodeCount(new NodeFilter({ owner: owner }));

expect(result).toStrictEqual(2);
expect(result).toStrictEqual(3);
expect(allNodesSpy).toHaveBeenCalledTimes(1);
});

Expand Down Expand Up @@ -311,8 +335,8 @@ describe('NodeService', () => {
const allNodesSpy = jest.spyOn(nodeService, 'getAllNodes').mockResolvedValueOnce(Promise.resolve(mockNodes));

const expectedVersions = {
'v1.2.38.0': 0.8866,
'v1.2.39.0': 0.1134,
'v1.2.38.0': 0.8878,
'v1.2.39.0': 0.1122,
};
const result = await nodeService.getNodeVersionsRaw();
expect(result).toStrictEqual(expectedVersions);
Expand Down
2 changes: 1 addition & 1 deletion src/test/unit/services/shards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('ShardService', () => {
const result = await service.getShards(new QueryPagination());
expect(result).toEqual(expect.arrayContaining([
expect.objectContaining(
{ shard: 1, validators: 24, activeValidators: 24 },
{ shard: 1, validators: 25, activeValidators: 25 },
),
expect.objectContaining(
{ shard: 0, validators: 24, activeValidators: 23 },
Expand Down
4 changes: 2 additions & 2 deletions src/test/unit/services/stake.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ describe('Stake Service', () => {

const result = await stakeService.getValidators();

expect(result.totalValidators).toEqual(97);
expect(result.activeValidators).toEqual(96);
expect(result.totalValidators).toEqual(98);
expect(result.activeValidators).toEqual(97);
expect(result.queueSize).toEqual(parseInt(Buffer.from(queueSize, 'base64').toString()));
});
});
Expand Down

0 comments on commit 9018295

Please sign in to comment.