Skip to content

Commit

Permalink
feat(core-api): add v2/node/fees endpoint (#2393)
Browse files Browse the repository at this point in the history
  • Loading branch information
faustbrian committed Apr 10, 2019
1 parent df9fd4d commit 606d06d
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 58 deletions.
Expand Up @@ -298,7 +298,7 @@ describe("Transactions Business Repository", () => {
describe("getFeeStatistics", () => {
it("should invoke getFeeStatistics on db repository", async () => {
databaseService.connection.transactionsRepository = {
getFeeStatistics: async minFeeBroadcast => minFeeBroadcast,
getFeeStatistics: async (days, minFeeBroadcast) => minFeeBroadcast,
getModel: () => new MockDatabaseModel(),
} as Database.ITransactionsRepository;
jest.spyOn(databaseService.connection.transactionsRepository, "getFeeStatistics").mockImplementation(
Expand All @@ -310,9 +310,9 @@ describe("Transactions Business Repository", () => {
},
});

await transactionsBusinessRepository.getFeeStatistics();
await transactionsBusinessRepository.getFeeStatistics(7);

expect(databaseService.connection.transactionsRepository.getFeeStatistics).toHaveBeenCalledWith(100);
expect(databaseService.connection.transactionsRepository.getFeeStatistics).toHaveBeenCalledWith(7, 100);
});
});

Expand Down
4 changes: 3 additions & 1 deletion packages/core-api/package.json
Expand Up @@ -41,6 +41,7 @@
"lodash.orderby": "^4.6.0",
"lodash.partition": "^4.6.0",
"lodash.snakecase": "^4.1.1",
"mathjs": "^5.9.0",
"semver": "^6.0.0"
},
"devDependencies": {
Expand All @@ -50,7 +51,8 @@
"@types/lodash.orderby": "^4.6.6",
"@types/lodash.partition": "^4.6.6",
"@types/lodash.snakecase": "^4.1.6",
"@types/semver": "^6.0.0"
"@types/semver": "^6.0.0",
"@types/mathjs": "^5.0.1"
},
"publishConfig": {
"access": "public"
Expand Down
30 changes: 0 additions & 30 deletions packages/core-api/src/repositories/transactions.ts
Expand Up @@ -257,36 +257,6 @@ export class TransactionsRepository extends Repository implements IRepository {
return this.__mapBlocksToTransactions(transactions);
}

/**
* Calculates min, max and average fee statistics based on transactions table
* @return {Object}
*/
public async getFeeStatistics(): Promise<any> {
const query = this.query
.select(
this.query.type,
this.query.fee.min("minFee"),
this.query.fee.max("maxFee"),
this.query.fee.avg("avgFee"),
this.query.timestamp.max("timestamp"),
)
.from(this.query)
.where(
this.query.timestamp.gte(
Crypto.slots.getTime(
dato()
.addDays(30)
.toMilliseconds(),
),
),
)
.and(this.query.fee.gte(this.transactionPool.options.dynamicFees.minFeeBroadcast))
.group(this.query.type)
.order('"timestamp" DESC');

return this._findMany(query);
}

/**
* Search all transactions.
*
Expand Down
5 changes: 0 additions & 5 deletions packages/core-api/src/versions/1/loader/controller.ts
@@ -1,7 +1,5 @@
import { app } from "@arkecosystem/core-container";
import Boom from "boom";
import Hapi from "hapi";
import { transactionsRepository } from "../../../repositories";
import { Controller } from "../shared/controller";

export class LoaderController extends Controller {
Expand Down Expand Up @@ -45,8 +43,6 @@ export class LoaderController extends Controller {

public async autoconfigure(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
const feeStatisticsData = await transactionsRepository.getFeeStatistics();

const network = this.config.get("network");

return super.respondWith({
Expand All @@ -57,7 +53,6 @@ export class LoaderController extends Controller {
explorer: network.client.explorer,
version: network.pubKeyHash,
ports: super.toResource(request, this.config, "ports"),
feeStatistics: super.toCollection(request, feeStatisticsData, "fee-statistics"),
},
});
} catch (error) {
Expand Down
34 changes: 29 additions & 5 deletions packages/core-api/src/versions/2/node/controller.ts
Expand Up @@ -2,6 +2,8 @@ import { app } from "@arkecosystem/core-container";
import { Database } from "@arkecosystem/core-interfaces";
import Boom from "boom";
import Hapi from "hapi";
import groupBy from "lodash.groupby";
import math from "mathjs";
import { Controller } from "../shared/controller";

export class NodeController extends Controller {
Expand Down Expand Up @@ -42,10 +44,6 @@ export class NodeController extends Controller {

public async configuration(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
const transactionsBusinessRepository = app.resolvePlugin<Database.IDatabaseService>("database")
.transactionsBusinessRepository;
const feeStatisticsData = await transactionsBusinessRepository.getFeeStatistics();

const network = this.config.get("network");
const dynamicFees = app.resolveOptions("transaction-pool").dynamicFees;

Expand All @@ -58,7 +56,6 @@ export class NodeController extends Controller {
version: network.pubKeyHash,
ports: super.toResource(request, this.config, "ports"),
constants: this.config.getMilestone(this.blockchain.getLastHeight()),
feeStatistics: super.toCollection(request, feeStatisticsData, "fee-statistics"),
transactionPool: {
maxTransactionAge: app.resolveOptions("transaction-pool").maxTransactionAge,
dynamicFees: dynamicFees.enabled ? dynamicFees : { enabled: false },
Expand All @@ -69,4 +66,31 @@ export class NodeController extends Controller {
return Boom.badImplementation(error);
}
}

public async fees(request: Hapi.Request) {
try {
const { transactionsBusinessRepository } = app.resolvePlugin<Database.IDatabaseService>("database");

// @ts-ignore
const results = await transactionsBusinessRepository.getFeeStatistics(request.query.days);

const resultsByDays = [];
for (const [type, transactions] of Object.entries(groupBy(results, "type"))) {
const fees = transactions.map(transaction => math.bignumber(transaction.fee));

resultsByDays.push({
type,
min: math.min(...fees).toFixed(0),
max: math.max(...fees).toFixed(0),
avg: math.mean(...fees).toFixed(0),
sum: math.sum(...fees).toFixed(0),
median: math.median(...fees).toFixed(0),
});
}

return { meta: { days: request.query.days }, data: resultsByDays };
} catch (error) {
return Boom.badImplementation(error);
}
}
}
10 changes: 10 additions & 0 deletions packages/core-api/src/versions/2/node/routes.ts
@@ -1,5 +1,6 @@
import Hapi from "hapi";
import { NodeController } from "./controller";
import * as Schema from "./schema";

export function registerRoutes(server: Hapi.Server): void {
const controller = new NodeController();
Expand All @@ -22,4 +23,13 @@ export function registerRoutes(server: Hapi.Server): void {
path: "/node/configuration",
handler: controller.configuration,
});

server.route({
method: "GET",
path: "/node/fees",
handler: controller.fees,
options: {
validate: Schema.fees,
},
});
}
12 changes: 12 additions & 0 deletions packages/core-api/src/versions/2/node/schema.ts
@@ -0,0 +1,12 @@
import joi from "joi";

export const fees: object = {
query: {
days: joi
.number()
.integer()
.min(1)
.max(30)
.default(7),
},
};
14 changes: 3 additions & 11 deletions packages/core-database-postgres/src/repositories/transactions.ts
Expand Up @@ -79,28 +79,20 @@ export class TransactionsRepository extends Repository implements Database.ITran
return new Transaction(this.pgp);
}

public getFeeStatistics(minFeeBroadcast: number): Promise<any> {
public async getFeeStatistics(days: number, minFeeBroadcast?: number): Promise<any> {
const query = this.query
.select(
this.query.type,
this.query.fee.min("minFee"),
this.query.fee.max("maxFee"),
this.query.fee.avg("avgFee"),
this.query.timestamp.max("timestamp"),
)
.select(this.query.type, this.query.fee, this.query.timestamp)
.from(this.query)
// Should make this '30' figure configurable
.where(
this.query.timestamp.gte(
Crypto.slots.getTime(
dato()
.subDays(30)
.subDays(days)
.toMilliseconds(),
),
),
)
.and(this.query.fee.gte(minFeeBroadcast))
.group(this.query.type)
.order('"timestamp" DESC');

return this.findMany(query);
Expand Down
Expand Up @@ -73,8 +73,9 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus
return this.mapBlocksToTransactions(rows);
}

public async getFeeStatistics() {
public async getFeeStatistics(days: number) {
return this.databaseServiceProvider().connection.transactionsRepository.getFeeStatistics(
days,
app.resolveOptions("transaction-pool").dynamicFees.minFeeBroadcast,
);
}
Expand Down
Expand Up @@ -23,7 +23,7 @@ export interface ITransactionsBusinessRepository {

findByTypeAndId(type: any, id: string): Promise<any>;

getFeeStatistics(): Promise<any>;
getFeeStatistics(days: number): Promise<any>;

search(params: IParameters): Promise<any>;
}
Expand Up @@ -37,7 +37,7 @@ export interface ITransactionsRepository extends IRepository {
totalAmount: Utils.Bignum;
}>;

getFeeStatistics(minFeeBroadcast: number): Promise<any>;
getFeeStatistics(days: number, minFeeBroadcast: number): Promise<any>;

/**
* Delete transactions with blockId
Expand Down
61 changes: 61 additions & 0 deletions yarn.lock
Expand Up @@ -2447,6 +2447,13 @@
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef"
integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==

"@types/mathjs@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/mathjs/-/mathjs-5.0.1.tgz#b98e163ea396b4f27bec20ee25ffb8fe9e656af8"
integrity sha512-EFBuueI+BRed9bnUO6/9my55b4FH+VQIvqMm58h9JGbtaGCkqr3YSDhnmVbM1SJjF//8SURERSypzNwejOk7lA==
dependencies:
decimal.js "^10.0.0"

"@types/micromatch@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-3.1.0.tgz#514c8a3d24b2680a9b838eeb80e6d7d724545433"
Expand Down Expand Up @@ -4656,6 +4663,11 @@ compare-versions@^3.2.1:
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26"
integrity sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==

complex.js@2.0.11:
version "2.0.11"
resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.0.11.tgz#09a873fbf15ffd8c18c9c2201ccef425c32b8bf1"
integrity sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==

component-emitter@1.2.1, component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
Expand Down Expand Up @@ -5147,6 +5159,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=

decimal.js@10.1.1, decimal.js@^10.0.0:
version "10.1.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.1.1.tgz#fb541922a2e8fc618f5e6fbb474e281ed3d2a64c"
integrity sha512-vEEgyk1fWVEnv7lPjkNedAIjzxQDue5Iw4FeX4UkNUDSVyD/jZTD0Bw2kAO7k6iyyJRAhM9oxxI0D1ET6k0Mmg==

decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
Expand Down Expand Up @@ -5669,6 +5686,11 @@ es6-promisify@^5.0.0:
dependencies:
es6-promise "^4.0.3"

escape-latex@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1"
integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==

escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
Expand Down Expand Up @@ -6197,6 +6219,11 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"

fraction.js@4.0.12:
version "4.0.12"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.12.tgz#0526d47c65a5fb4854df78bc77f7bec708d7b8c3"
integrity sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==

fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
Expand Down Expand Up @@ -7721,6 +7748,11 @@ items@2.x.x:
resolved "https://registry.yarnpkg.com/items/-/items-2.1.2.tgz#0849354595805d586dac98e7e6e85556ea838558"
integrity sha512-kezcEqgB97BGeZZYtX/MA8AG410ptURstvnz5RAgyFZ8wQFPMxHY8GpTq+/ZHKT3frSlIthUq7EvLt9xn3TvXg==

javascript-natural-sort@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=

jest-changed-files@^24.7.0:
version "24.7.0"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.7.0.tgz#39d723a11b16ed7b373ac83adc76a69464b0c4fa"
Expand Down Expand Up @@ -9061,6 +9093,20 @@ math-random@^1.0.1:
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==

mathjs@^5.9.0:
version "5.9.0"
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-5.9.0.tgz#e926feb1c67c2cae103512dd35ddc2573106e1e5"
integrity sha512-f1xmJklkTCr48y023cFy/ZSoVzOfgHp1gutvebi/Vv5RLly6j8G9T2/XHkfXewZKcwPDbhBkFEYljaCjudxulQ==
dependencies:
complex.js "2.0.11"
decimal.js "10.1.1"
escape-latex "1.2.0"
fraction.js "4.0.12"
javascript-natural-sort "0.7.1"
seed-random "2.2.0"
tiny-emitter "2.1.0"
typed-function "1.1.0"

md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
Expand Down Expand Up @@ -11794,6 +11840,11 @@ secure-keys@^1.0.0:
resolved "https://registry.yarnpkg.com/secure-keys/-/secure-keys-1.0.0.tgz#f0c82d98a3b139a8776a8808050b824431087fca"
integrity sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=

seed-random@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=

semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
Expand Down Expand Up @@ -13025,6 +13076,11 @@ timezone-support@^1.8.0:
dependencies:
commander "2.19.0"

tiny-emitter@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==

tiny-glob@^0.2.6:
version "0.2.6"
resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.6.tgz#9e056e169d9788fe8a734dfa1ff02e9b92ed7eda"
Expand Down Expand Up @@ -13290,6 +13346,11 @@ typechecker@^4.0.1, typechecker@^4.3.0:
dependencies:
editions "^2.1.0"

typed-function@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-1.1.0.tgz#ea149706e0fb42aca1791c053a6d94ccd6c4fdcb"
integrity sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==

typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
Expand Down

0 comments on commit 606d06d

Please sign in to comment.