Skip to content

Commit

Permalink
fix(core-blockchain): stuck at not ready to accept new block (#2139)
Browse files Browse the repository at this point in the history
  • Loading branch information
spkjp authored and faustbrian committed Feb 22, 2019
1 parent 38f6833 commit 2a9bbdd
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
6 changes: 5 additions & 1 deletion packages/core-blockchain/src/blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,13 @@ export class Blockchain implements blockchain.IBlockchain {
/**
* Fork the chain at the given block.
*/
public forkBlock(block: models.Block): void {
public forkBlock(block: models.Block, numberOfBlockToRollback?: number): void {
this.state.forkedBlock = block;

if (numberOfBlockToRollback) {
this.state.numberOfBlocksToRollback = numberOfBlockToRollback;
}

this.dispatch("FORK");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// tslint:disable:max-classes-per-file

import { app } from "@arkecosystem/core-container";
import { models } from "@arkecosystem/crypto";
import { Blockchain } from "../../blockchain";
Expand All @@ -6,14 +8,48 @@ import { BlockHandler } from "./block-handler";

enum UnchainedBlockStatus {
NotReadyToAcceptNewHeight,
ExceededNotReadyToAcceptNewHeightMaxAttempts,
AlreadyInBlockchain,
EqualToLastBlock,
GeneratorMismatch,
DoubleForging,
InvalidTimestamp,
}

class BlockNotReadyCounter {
public static maxAttempts = 5;

private id = "";
private attempts = 0;

public increment(block: models.Block): boolean {
const { id } = block.data;
let attemptsLeft = false;

if (this.id !== id) {
this.reset();
this.id = id;
}

this.attempts += 1;

attemptsLeft = this.attempts <= BlockNotReadyCounter.maxAttempts;
if (!attemptsLeft) {
this.reset();
}

return attemptsLeft;
}

public reset() {
this.attempts = 0;
this.id = "";
}
}

export class UnchainedHandler extends BlockHandler {
public static notReadyCounter = new BlockNotReadyCounter();

public constructor(
protected blockchain: Blockchain,
protected block: models.Block,
Expand All @@ -39,6 +75,11 @@ export class UnchainedHandler extends BlockHandler {
return BlockProcessorResult.Rejected;
}

case UnchainedBlockStatus.ExceededNotReadyToAcceptNewHeightMaxAttempts: {
this.blockchain.forkBlock(this.block, 5000); // TODO: find a better heuristic based on peer information
return BlockProcessorResult.DiscardedButCanBeBroadcasted;
}

case UnchainedBlockStatus.GeneratorMismatch:
case UnchainedBlockStatus.InvalidTimestamp: {
return BlockProcessorResult.Rejected;
Expand All @@ -65,7 +106,19 @@ export class UnchainedHandler extends BlockHandler {
this.logger.debug(`Discarded ${this.blockchain.processQueue.length()} downloaded blocks.`);
}

return UnchainedBlockStatus.NotReadyToAcceptNewHeight;
// If we consecutively fail to accept the same block, our chain is likely forked. In this
// case `increment` returns false.
if (UnchainedHandler.notReadyCounter.increment(this.block)) {
return UnchainedBlockStatus.NotReadyToAcceptNewHeight;
}

this.logger.debug(
`Blockchain is still not ready to accept block at height ${this.block.data.height.toLocaleString()} after ${
BlockNotReadyCounter.maxAttempts
} tries. Going to rollback. :warning:`,
);

return UnchainedBlockStatus.ExceededNotReadyToAcceptNewHeightMaxAttempts;
} else if (this.block.data.height < lastBlock.data.height) {
this.logger.debug(
`Block ${this.block.data.height.toLocaleString()} disregarded because already in blockchain :warning:`,
Expand Down

0 comments on commit 2a9bbdd

Please sign in to comment.