diff --git a/__tests__/unit/core-blockchain/blockchain.test.ts b/__tests__/unit/core-blockchain/blockchain.test.ts index fab0941b3c..9b68bb8e65 100644 --- a/__tests__/unit/core-blockchain/blockchain.test.ts +++ b/__tests__/unit/core-blockchain/blockchain.test.ts @@ -238,6 +238,120 @@ describe("Blockchain", () => { blockchain.enqueueBlocks = enqueueBlocks; }); + it("should handle block from forger if in right slot", () => { + blockchain.state.started = true; + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: Crypto.Slots.getSlotTime(Crypto.Slots.getSlotNumber()), + }; + + // @ts-ignore + blockchain.handleIncomingBlock(block, false); + + expect(blockchain.dispatch).toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }); + + it("should not handle block from forger if in wrong slot", () => { + blockchain.state.started = true; + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: Crypto.Slots.getSlotTime(Crypto.Slots.getSlotNumber() - 1), + }; + + // @ts-ignore + blockchain.handleIncomingBlock(block, true); + + expect(blockchain.dispatch).not.toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).not.toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }); + + it("should handle block if not from forger if in wrong slot", () => { + blockchain.state.started = true; + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: Crypto.Slots.getSlotTime(Crypto.Slots.getSlotNumber() - 1), + }; + + // @ts-ignore + blockchain.handleIncomingBlock(block, false); + + expect(blockchain.dispatch).toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }); + + it("should not handle block from forger if less than 2 seconds left in slot", async () => { + blockchain.state.started = true; + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: Crypto.Slots.getSlotTime(Crypto.Slots.getSlotNumber()), + }; + + await delay(Crypto.Slots.getTimeInMsUntilNextSlot() - 1000); + + // @ts-ignore + blockchain.handleIncomingBlock(block, true); + + expect(blockchain.dispatch).not.toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).not.toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }, 10000); + + it("should handle block if not from forger if less than 2 seconds left in slot", async () => { + blockchain.state.started = true; + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: Crypto.Slots.getSlotTime(Crypto.Slots.getSlotNumber()), + }; + + await delay(Crypto.Slots.getTimeInMsUntilNextSlot() - 1000); + + // @ts-ignore + blockchain.handleIncomingBlock(block, false); + + expect(blockchain.dispatch).toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }, 10000); + it("should disregard block when blockchain is not ready", async () => { blockchain.state.started = false; const loggerInfo = jest.spyOn(logger, "info"); diff --git a/packages/core-blockchain/src/blockchain.ts b/packages/core-blockchain/src/blockchain.ts index 9af8874e1b..462ef357c6 100644 --- a/packages/core-blockchain/src/blockchain.ts +++ b/packages/core-blockchain/src/blockchain.ts @@ -238,6 +238,15 @@ export class Blockchain implements blockchain.IBlockchain { const currentSlot: number = Crypto.Slots.getSlotNumber(); const receivedSlot: number = Crypto.Slots.getSlotNumber(block.timestamp); + if (fromForger) { + const minimumMs: number = 2000; + const timeLeftInMs: number = Crypto.Slots.getTimeInMsUntilNextSlot(); + if (currentSlot !== receivedSlot || timeLeftInMs < minimumMs) { + logger.info(`Discarded block ${block.height.toLocaleString()} because it was received too late.`); + return; + } + } + if (receivedSlot > currentSlot) { logger.info(`Discarded block ${block.height.toLocaleString()} because it takes a future slot.`); return; diff --git a/packages/core-p2p/src/socket-server/worker.ts b/packages/core-p2p/src/socket-server/worker.ts index 84b97c6bf4..cf7e1e237e 100644 --- a/packages/core-p2p/src/socket-server/worker.ts +++ b/packages/core-p2p/src/socket-server/worker.ts @@ -1,5 +1,7 @@ import { P2P } from "@arkecosystem/core-interfaces"; import Ajv from "ajv"; +import delay from "delay"; + import { cidr } from "ip"; import { RateLimiter } from "../rate-limiter"; import { buildRateLimiter } from "../utils"; @@ -360,7 +362,7 @@ export class Worker extends SCWorker { req.socket.terminate(); return; } - + await delay(1); next(); }