Skip to content

Commit

Permalink
feat(core-api): add active delegates endpoint (#2205)
Browse files Browse the repository at this point in the history
  • Loading branch information
dated authored and faustbrian committed Mar 6, 2019
1 parent ea06ba4 commit 7889947
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 17 deletions.
31 changes: 31 additions & 0 deletions __tests__/integration/core-api/v2/handlers/delegates.test.ts
Expand Up @@ -136,6 +136,37 @@ describe("API 2.0 - Delegates", () => {
);
});

describe("GET /delegates/active", () => {
describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])(
"using the %s header",
(header, request) => {
it("should GET all the active delegates at the current network height", async () => {
const activeDelegates = app.getConfig().getMilestone().activeDelegates;

const response = await utils[request]("GET", "delegates/active");
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();
expect(response.data.data).toHaveLength(activeDelegates);

response.data.data.forEach(utils.expectDelegate);
expect(response.data.data.sort((a, b) => a.rank < b.rank)).toEqual(response.data.data);
});

it("should GET all the active delegates at the specified height", async () => {
const activeDelegates = app.getConfig().getMilestone(1).activeDelegates;

const response = await utils[request]("GET", "delegates/active", { height: 1 });
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();
expect(response.data.data).toHaveLength(activeDelegates);

response.data.data.forEach(utils.expectDelegate);
expect(response.data.data.sort((a, b) => a.rank < b.rank)).toEqual(response.data.data);
});
},
);
});

describe("GET /delegates/:id", () => {
describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])(
"using the %s header",
Expand Down
Expand Up @@ -405,10 +405,7 @@ describe("Delegate Repository", () => {

expect(results).toBeArray();
expect(results[0].username).toBeString();
expect(results[0].approval).toBeNumber();
expect(results[0].productivity).toBeNumber();
expect(results[0].approval).toBe(delegateCalculator.calculateApproval(delegate, height));
expect(results[0].productivity).toBe(delegateCalculator.calculateProductivity(delegate));
expect(results[0].username).toEqual(delegate.username);
});
});
});
11 changes: 11 additions & 0 deletions packages/core-api/src/versions/2/delegates/controller.ts
Expand Up @@ -14,6 +14,17 @@ export class DelegatesController extends Controller {
}
}

public async active(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
// @ts-ignore
const data = await request.server.methods.v2.delegates.active(request);

return super.respondWithCache(data, h);
} catch (error) {
return Boom.badImplementation(error);
}
}

public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
// @ts-ignore
Expand Down
23 changes: 21 additions & 2 deletions packages/core-api/src/versions/2/delegates/methods.ts
@@ -1,12 +1,14 @@
import { app } from "@arkecosystem/core-container";
import { Database } from "@arkecosystem/core-interfaces";
import { Blockchain, Database } from "@arkecosystem/core-interfaces";
import { orderBy } from "@arkecosystem/utils";
import Boom from "boom";
import { blocksRepository } from "../../../repositories";
import { ServerCache } from "../../../services";
import { paginate, respondWithResource, toPagination } from "../utils";
import { paginate, respondWithResource, respondWithCollection, toPagination } from "../utils";

const config = app.getConfig();
const databaseService = app.resolvePlugin<Database.IDatabaseService>("database");
const blockchain = app.resolvePlugin<Blockchain.IBlockchain>("blockchain");

const index = async request => {
const delegates = await databaseService.delegates.findAll({
Expand All @@ -17,6 +19,18 @@ const index = async request => {
return toPagination(request, delegates, "delegate");
};

const active = async request => {
const delegates = await databaseService.delegates.getActiveAtHeight(
request.query.height || blockchain.getLastHeight()
);

if (!delegates.length) {
return Boom.notFound("Delegates not found");
}

return respondWithCollection(request, delegates, "delegate");
};

const show = async request => {
const delegate = await databaseService.delegates.findById(request.params.id);

Expand Down Expand Up @@ -82,11 +96,16 @@ const voterBalances = async request => {
};

export function registerMethods(server) {
const { activeDelegates, blocktime } = config.getMilestone();

ServerCache.make(server)
.method("v2.delegates.index", index, 8, request => ({
...request.query,
...paginate(request),
}))
.method("v2.delegates.active", active, activeDelegates * blocktime, request => ({
...request.query
}))
.method("v2.delegates.show", show, 8, request => ({ id: request.params.id }))
.method("v2.delegates.search", search, 30, request => ({
...request.payload,
Expand Down
9 changes: 9 additions & 0 deletions packages/core-api/src/versions/2/delegates/routes.ts
Expand Up @@ -15,6 +15,15 @@ export function registerRoutes(server: Hapi.Server): void {
},
});

server.route({
method: "GET",
path: "/delegates/active",
handler: controller.active,
options: {
validate: Schema.active,
},
});

server.route({
method: "GET",
path: "/delegates/{id}",
Expand Down
8 changes: 8 additions & 0 deletions packages/core-api/src/versions/2/delegates/schema.ts
Expand Up @@ -74,6 +74,14 @@ export const index: object = {
},
};

export const active: object = {
query: {
height: Joi.number()
.integer()
.min(1),
}
};

export const show: object = {
params: {
id: schemaIdentifier,
Expand Down
Expand Up @@ -150,13 +150,7 @@ export class DelegatesBusinessRepository implements Database.IDelegatesBusinessR
const delegates = await this.databaseServiceProvider().getActiveDelegates(height);

return delegates.map(delegate => {
const wallet = this.databaseServiceProvider().wallets.findById(delegate.publicKey);

return {
username: wallet.username,
approval: delegateCalculator.calculateApproval(delegate, height),
productivity: delegateCalculator.calculateProductivity(wallet),
};
return this.databaseServiceProvider().wallets.findById(delegate.publicKey);
});
}

Expand Down
Expand Up @@ -10,5 +10,5 @@ export interface IDelegatesBusinessRepository {

findById(id: string): models.Wallet;

getActiveAtHeight(height: number): Promise<Array<{ username: string; approval: number; productivity: number }>>;
getActiveAtHeight(height: number): Promise<models.Wallet[]>;
}
6 changes: 3 additions & 3 deletions packages/core-p2p/src/peer-verifier.ts
Expand Up @@ -300,7 +300,7 @@ export class PeerVerifier {
// the last block in a round (so that the delegates calculations are still the same for
// both chains).

const delegates = await this.getDelegates(round);
const delegates = await this.getDelegatesByRound(round);

const hisBlocksByHeight = {};

Expand All @@ -324,10 +324,10 @@ export class PeerVerifier {

/**
* Get the delegates for the given round.
* @param {Object} round round to get delegates for
* @param {Object} round to get delegates for
* @return {Object} a map of { publicKey: delegate, ... } of all delegates for the given round
*/
private async getDelegates(round: any): Promise<any> {
private async getDelegatesByRound(round: any): Promise<any> {
const numDelegates = round.maxDelegates;

const heightOfFirstBlockInRound = (round.round - 1) * numDelegates + 1;
Expand Down

0 comments on commit 7889947

Please sign in to comment.