diff --git a/__tests__/unit/core-p2p/socket-server/controllers/peer.test.ts b/__tests__/unit/core-p2p/socket-server/controllers/peer.test.ts index b7fa1c4368..05445f272b 100644 --- a/__tests__/unit/core-p2p/socket-server/controllers/peer.test.ts +++ b/__tests__/unit/core-p2p/socket-server/controllers/peer.test.ts @@ -1,11 +1,10 @@ -import { Crypto, Blocks, Utils, Networks, Transactions, Identities, Managers } from "@arkecosystem/crypto"; import { Container } from "@arkecosystem/core-kernel"; - -import { PeerController } from "@arkecosystem/core-p2p/src/socket-server/controllers/peer"; -import { Peer } from "@arkecosystem/core-p2p/src/peer"; import { MissingCommonBlockError } from "@arkecosystem/core-p2p/src/errors"; -import { getPeerConfig } from "@arkecosystem/core-p2p/src/socket-server/utils/get-peer-config"; +import { Peer } from "@arkecosystem/core-p2p/src/peer"; +import { PeerController } from "@arkecosystem/core-p2p/src/socket-server/controllers/peer"; import { TooManyTransactionsError, UnchainedBlockError } from "@arkecosystem/core-p2p/src/socket-server/errors"; +import { getPeerConfig } from "@arkecosystem/core-p2p/src/socket-server/utils/get-peer-config"; +import { Blocks, Crypto, Identities, Managers, Networks, Transactions, Utils } from "@arkecosystem/crypto"; Managers.configManager.getMilestone().aip11 = true; // for creating aip11 v2 transactions @@ -24,14 +23,14 @@ describe("PeerController", () => { getLastDownloadedBlock: jest.fn(), }; const createProcessor = jest.fn(); - const appPlugins = [ { package: "@arkecosystem/core-api", options: {} } ]; + const appPlugins = [{ package: "@arkecosystem/core-api", options: {} }]; const coreApiServiceProvider = { name: () => "core-api", configDefaults: () => ({ - server: { http: { port: 4003 } } + server: { http: { port: 4003 } }, }), }; - const serviceProviders = { "@arkecosystem/core-api": coreApiServiceProvider, }; + const serviceProviders = { "@arkecosystem/core-api": coreApiServiceProvider }; const configRepository = { get: () => appPlugins }; // get("app.plugins") const serviceProviderRepository = { get: (plugin) => serviceProviders[plugin] }; const appGet = { @@ -48,10 +47,15 @@ describe("PeerController", () => { resolve: () => ({ from: () => ({ merge: () => ({ - all: () => ({ server: { http: { port: "4003" } } }) - }) - }) - }) + all: () => ({ + server: { http: { port: "4003" } }, + options: { + estimateTotalCount: true, + }, + }), + }), + }), + }), }; beforeAll(() => { @@ -191,7 +195,7 @@ describe("PeerController", () => { .recipientId(Identities.Address.fromPassphrase("recipient's secret")) .fee("100") .sign("sender's secret") - .build() + .build(), ], } as Blocks.Block; const deepClone = (obj) => JSON.parse(JSON.stringify(obj)); @@ -202,7 +206,7 @@ describe("PeerController", () => { blockTooManyTxs.data.numberOfTransactions = 350; const blockSerialized = Blocks.Serializer.serializeWithTransactions({ ...blockTooManyTxs.data, - transactions: blockTooManyTxs.transactions.map(tx => tx.data) + transactions: blockTooManyTxs.transactions.map((tx) => tx.data), }); await expect( @@ -215,16 +219,16 @@ describe("PeerController", () => { const blockTooManyTxs = deepClone(block); const transactions = []; - for(let i = 0; i < 2; i++) { + for (let i = 0; i < 2; i++) { transactions.push( Transactions.BuilderFactory.transfer() .version(2) .amount("100") .recipientId(Identities.Address.fromPassphrase(`recipient secret ${i}`)) .fee("100") - .nonce(`${i+1}`) + .nonce(`${i + 1}`) .sign(`sender secret ${i}`) - .build() + .build(), ); } blockTooManyTxs.transactions = transactions; @@ -232,38 +236,38 @@ describe("PeerController", () => { const blockSerialized = Blocks.Serializer.serializeWithTransactions({ ...blockTooManyTxs.data, - transactions: transactions.map(tx => tx.data), + transactions: transactions.map((tx) => tx.data), }); // this is a trick to make the first numberOfTransactions check pass // but then transactions.length fail // probably some unreachable code though... const milestone = Managers.configManager.getMilestone(); - const spyGetMilestone = jest.spyOn(Managers.configManager, "getMilestone") + const spyGetMilestone = jest.spyOn(Managers.configManager, "getMilestone"); for (let i = 0; i < 71; i++) { // yeah 71 times :wtf: before the one we are interested to mock kicks in spyGetMilestone.mockReturnValueOnce({ ...milestone, block: { - maxTransactions: 150 - } - }) + maxTransactions: 150, + }, + }); } spyGetMilestone.mockReturnValueOnce({ ...milestone, block: { - maxTransactions: 1 - } - }) + maxTransactions: 1, + }, + }); await expect( peerController.postBlock( { payload: { block: { data: blockSerialized } }, - info: { remoteAddress: "187.55.33.22" } + info: { remoteAddress: "187.55.33.22" }, }, - {} - ) + {}, + ), ).rejects.toBeInstanceOf(TooManyTransactionsError); spyGetMilestone.mockRestore(); @@ -271,40 +275,42 @@ describe("PeerController", () => { }); describe("when block is not chained", () => { - it.each([[true], [false]]) - ("should throw UnchainedBlockError only if block is not known", async (blockPing) => { - blockchain.getLastDownloadedBlock = jest.fn().mockReturnValueOnce(Networks.testnet.genesisBlock); - const blockUnchained = deepClone(block); - blockUnchained.data.height = 9; - const blockSerialized = Blocks.Serializer.serializeWithTransactions({ - ...blockUnchained.data, - transactions: blockUnchained.transactions.map(tx => tx.data) - }); - - if (blockPing) { - blockchain.pingBlock = jest.fn().mockReturnValueOnce(true); - await expect( - peerController.postBlock( - { - payload: { block: { data: blockSerialized } }, - info: { remoteAddress: "187.55.33.22" }, - }, - {}, - ), - ).toResolve(); - expect(blockchain.handleIncomingBlock).toBeCalledTimes(0); - } else { - await expect( - peerController.postBlock( - { - payload: { block: { data: blockSerialized } }, - info: { remoteAddress: "187.55.33.22" }, - }, - {}, - ), - ).rejects.toBeInstanceOf(UnchainedBlockError); - } - }); + it.each([[true], [false]])( + "should throw UnchainedBlockError only if block is not known", + async (blockPing) => { + blockchain.getLastDownloadedBlock = jest.fn().mockReturnValueOnce(Networks.testnet.genesisBlock); + const blockUnchained = deepClone(block); + blockUnchained.data.height = 9; + const blockSerialized = Blocks.Serializer.serializeWithTransactions({ + ...blockUnchained.data, + transactions: blockUnchained.transactions.map((tx) => tx.data), + }); + + if (blockPing) { + blockchain.pingBlock = jest.fn().mockReturnValueOnce(true); + await expect( + peerController.postBlock( + { + payload: { block: { data: blockSerialized } }, + info: { remoteAddress: "187.55.33.22" }, + }, + {}, + ), + ).toResolve(); + expect(blockchain.handleIncomingBlock).toBeCalledTimes(0); + } else { + await expect( + peerController.postBlock( + { + payload: { block: { data: blockSerialized } }, + info: { remoteAddress: "187.55.33.22" }, + }, + {}, + ), + ).rejects.toBeInstanceOf(UnchainedBlockError); + } + }, + ); }); describe("when block comes from forger", () => { @@ -315,7 +321,7 @@ describe("PeerController", () => { const blockSerialized = Blocks.Serializer.serializeWithTransactions({ ...block.data, - transactions: block.transactions.map(tx => tx.data) + transactions: block.transactions.map((tx) => tx.data), }); await peerController.postBlock( { @@ -339,7 +345,7 @@ describe("PeerController", () => { const blockSerialized = Blocks.Serializer.serializeWithTransactions({ ...block.data, - transactions: block.transactions.map(tx => tx.data) + transactions: block.transactions.map((tx) => tx.data), }); await peerController.postBlock( { diff --git a/__tests__/unit/core-p2p/socket-server/utils/get-peer-config.test.ts b/__tests__/unit/core-p2p/socket-server/utils/get-peer-config.test.ts index 0476dbbcc9..c8e644b9b6 100644 --- a/__tests__/unit/core-p2p/socket-server/utils/get-peer-config.test.ts +++ b/__tests__/unit/core-p2p/socket-server/utils/get-peer-config.test.ts @@ -1,6 +1,6 @@ -import { Managers } from "@arkecosystem/crypto"; -import { getPeerConfig } from "@arkecosystem/core-p2p/src/socket-server/utils/get-peer-config"; import { Container } from "@arkecosystem/core-kernel"; +import { getPeerConfig } from "@arkecosystem/core-p2p/src/socket-server/utils/get-peer-config"; +import { Managers } from "@arkecosystem/crypto"; describe("getPeerConfig", () => { const mockConfig = { @@ -17,27 +17,33 @@ describe("getPeerConfig", () => { const appPlugins = [ { package: "@arkecosystem/core-api", options: {} }, { package: "@arkecosystem/core-webhooks" }, + { package: "@arkecosystem/core-p2p" }, ]; const coreApiServiceProvider = { name: () => "core-api", configDefaults: () => ({ - server: { http: { port: 4003 } } + server: { http: { port: 4003 } }, }), }; const coreWebhooksServiceProvider = { name: () => "core-webhooks", configDefaults: () => ({}), }; + const coreP2PServiceProvider = { + name: () => "core-p2p", + configDefaults: () => ({}), + }; const serviceProviders = { "@arkecosystem/core-api": coreApiServiceProvider, "@arkecosystem/core-webhooks": coreWebhooksServiceProvider, - } + "@arkecosystem/core-p2p": coreP2PServiceProvider, + }; const configRepository = { get: () => appPlugins }; // get("app.plugins") const serviceProviderRepository = { get: (plugin) => serviceProviders[plugin] }; const appGet = { [Container.Identifiers.ConfigRepository]: configRepository, [Container.Identifiers.ServiceProviderRepository]: serviceProviderRepository, - } + }; const app = { version: () => version, get: (key) => appGet[key], @@ -47,22 +53,25 @@ describe("getPeerConfig", () => { all: () => ({ server: { http: { - port: "4003" - } - } - }) - }) + port: "4003", + }, + }, + options: { + estimateTotalCount: true, + }, + }), + }), }), discover: () => ({ merge: () => ({ all: () => ({ server: { - port: "4004" - } - }) - }) - }) - }) + port: "4004", + }, + }), + }), + }), + }), }; it("should return own config from config manager", () => { @@ -81,12 +90,13 @@ describe("getPeerConfig", () => { plugins: { "@arkecosystem/core-api": { enabled: true, - port: 4003 + estimateTotalCount: true, + port: 4003, }, "@arkecosystem/core-webhooks": { enabled: true, - port: 4004 - } + port: 4004, + }, }, }); }); diff --git a/packages/core-api/src/resources/peer.ts b/packages/core-api/src/resources/peer.ts index c353180ed0..6be047df9e 100644 --- a/packages/core-api/src/resources/peer.ts +++ b/packages/core-api/src/resources/peer.ts @@ -30,6 +30,7 @@ export class PeerResource implements Resource { version: resource.version, height: resource.state ? resource.state.height : resource.height, latency: resource.latency, + plugins: resource.plugins, }; } } diff --git a/packages/core-kernel/src/contracts/p2p/peer.ts b/packages/core-kernel/src/contracts/p2p/peer.ts index 9d3742973d..a915d75eb2 100644 --- a/packages/core-kernel/src/contracts/p2p/peer.ts +++ b/packages/core-kernel/src/contracts/p2p/peer.ts @@ -5,7 +5,7 @@ export interface PeerPorts { } export interface PeerPlugins { - [name: string]: { enabled: boolean; port: number }; + [name: string]: { enabled: boolean; port: number; estimateTotalCount?: boolean }; } export interface Peer { diff --git a/packages/core-p2p/src/schemas.ts b/packages/core-p2p/src/schemas.ts index bd778bbc53..bccafaaafd 100644 --- a/packages/core-p2p/src/schemas.ts +++ b/packages/core-p2p/src/schemas.ts @@ -252,6 +252,9 @@ export const replySchemas = { enabled: { type: "boolean", }, + estimateTotalCount: { + type: "boolean", + }, }, }, }, diff --git a/packages/core-p2p/src/socket-server/utils/get-peer-config.ts b/packages/core-p2p/src/socket-server/utils/get-peer-config.ts index f7bf35f0e9..86f28279ad 100644 --- a/packages/core-p2p/src/socket-server/utils/get-peer-config.ts +++ b/packages/core-p2p/src/socket-server/utils/get-peer-config.ts @@ -25,6 +25,10 @@ const transformPlugins = (plugins: PluginConfig[]): Contracts.P2P.PeerPlugins => enabled: true, // default to true because "enabled" flag is in different place based on which plugin port, }; + + if (name.includes("core-api")) { + result[name].estimateTotalCount = pluginConfig.options.options.estimateTotalCount; + } } return result; diff --git a/yarn.lock b/yarn.lock index f85f40312b..6abb1c4930 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1775,7 +1775,7 @@ resolved "https://registry.yarnpkg.com/@hapi/teamwork/-/teamwork-4.0.0.tgz#4aadbb88de13bce39ab8431565cd50c7ecc6327f" integrity sha512-V6xYOrr5aFv/IJqNPneaYCu8vuGTKisamqHVRS3JJnbZr18TrpXdsJOYk9pjPhFti+M2YETPebQLUr820N5NoQ== -"@hapi/teamwork@5.x": +"@hapi/teamwork@5.x.x": version "5.1.0" resolved "https://registry.yarnpkg.com/@hapi/teamwork/-/teamwork-5.1.0.tgz#7801a61fc727f702fd2196ef7625eb4e389f4124" integrity sha512-llqoQTrAJDTXxG3c4Kz/uzhBS1TsmSBa/XG5SPcVXgmffHE1nFtyLIK0hNJHCB3EuBKT84adzd1hZNY9GJLWtg==