Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #5438 from LiskHQ/4942-add_banning
Browse files Browse the repository at this point in the history
Add banning for the missedBlock, and forged height diff conditions - Closes #4942
  • Loading branch information
shuse2 authored Jun 16, 2020
2 parents aea0da1 + 84de3d9 commit ca9ec5b
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 0 deletions.
11 changes: 11 additions & 0 deletions elements/lisk-dpos/src/delegates_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ interface DelegatesInfoConstructor {

const _isGenesisBlock = (header: BlockHeader): boolean => header.height === 1;
const zeroRandomSeed = Buffer.from('00000000000000000000000000000000', 'hex');
const maxConsecutiveMissedBlocks = 50;
const maxLastForgedHeightDiff = 260000;

export class DelegatesInfo {
private readonly chain: Chain;
Expand Down Expand Up @@ -192,6 +194,15 @@ export class DelegatesInfo {
const missedForgerAddress = expectedForgingAddresses[index];
const missedForger = await stateStore.account.get(missedForgerAddress);
missedForger.asset.delegate.consecutiveMissedBlocks += 1;
// Ban the missed forger if both consecutive missed block and last forged blcok diff condition are met
if (
missedForger.asset.delegate.consecutiveMissedBlocks >
maxConsecutiveMissedBlocks &&
blockHeader.height - missedForger.asset.delegate.lastForgedHeight >
maxLastForgedHeightDiff
) {
missedForger.asset.delegate.isBanned = true;
}
stateStore.account.set(missedForgerAddress, missedForger);
}
// Reset consecutive missed block
Expand Down
228 changes: 228 additions & 0 deletions elements/lisk-dpos/test/unit/apply.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,234 @@ describe('dpos.apply()', () => {
expect(forger.asset.delegate.lastForgedHeight).toEqual(block.height);
});
});

describe('when forger missed a block has 50 consecutive missed block, but forged within 260k blocks', () => {
it('should not ban the missed forger', async () => {
const forgedDelegate = forgedDelegates[forgedDelegates.length - 1];
// Arrange
const lastBlock = {
generatorPublicKey: forgedDelegate.publicKey,
height: 920006,
timestamp: 10000270,
} as BlockHeader;
block = {
height: 920007,
timestamp: 10000290,
generatorPublicKey: forgedDelegate.publicKey,
} as BlockHeader;
const forgerIndex = forgersList.forgersList[0].delegates.findIndex(
forger => forger.equals(forgedDelegate.address),
);
const missedDelegate = forgedDelegates[forgerIndex - 1];
forgersList = {
forgersList: [
{
round: 8933,
delegates: [...forgedDelegates.map(d => d.address)],
standby: [],
},
],
};
delegateVoteWeights = {
voteWeights: [
{
round: 10,
delegates: forgedDelegates.map(d => ({
address: d.address,
voteWeight: d.asset.delegate.totalVotesReceived,
})),
},
],
};
stateStore = new StateStoreMock(
[
...forgedDelegates.map(forger => {
if (forger.address.equals(missedDelegate.address)) {
// eslint-disable-next-line no-param-reassign
forger.asset.delegate.lastForgedHeight =
block.height - 260000 + 5000;
// eslint-disable-next-line no-param-reassign
forger.asset.delegate.consecutiveMissedBlocks = 50;
}
return forger;
}),
],
{
[CONSENSUS_STATE_DELEGATE_FORGERS_LIST]: codec.encode(
forgerListSchema,
forgersList,
),
[CONSENSUS_STATE_DELEGATE_VOTE_WEIGHTS]: codec.encode(
voteWeightsSchema,
delegateVoteWeights,
),
},
{ lastBlockHeaders: [lastBlock] },
);
// Act
await dpos.apply(block, stateStore);

const updatedMissedForger = await stateStore.account.get(
missedDelegate.address,
);
expect(updatedMissedForger.asset.delegate.isBanned).toBeFalse();
expect(
updatedMissedForger.asset.delegate.consecutiveMissedBlocks,
).toEqual(51);
});
});

describe('when forger missed a block has not forged within 260k blocks, but does not have 50 consecutive missed block', () => {
it('should not ban the missed forger', async () => {
const forgedDelegate = forgedDelegates[forgedDelegates.length - 1];
// Arrange
const lastBlock = {
generatorPublicKey: forgedDelegate.publicKey,
height: 920006,
timestamp: 10000270,
} as BlockHeader;
block = {
height: 920007,
timestamp: 10000290,
generatorPublicKey: forgedDelegate.publicKey,
} as BlockHeader;
const forgerIndex = forgersList.forgersList[0].delegates.findIndex(
forger => forger.equals(forgedDelegate.address),
);
const missedDelegate = forgedDelegates[forgerIndex - 1];
forgersList = {
forgersList: [
{
round: 8933,
delegates: [...forgedDelegates.map(d => d.address)],
standby: [],
},
],
};
delegateVoteWeights = {
voteWeights: [
{
round: 10,
delegates: forgedDelegates.map(d => ({
address: d.address,
voteWeight: d.asset.delegate.totalVotesReceived,
})),
},
],
};
stateStore = new StateStoreMock(
[
...forgedDelegates.map(forger => {
if (forger.address.equals(missedDelegate.address)) {
// eslint-disable-next-line no-param-reassign
forger.asset.delegate.lastForgedHeight =
block.height - 260000 - 1;
// eslint-disable-next-line no-param-reassign
forger.asset.delegate.consecutiveMissedBlocks = 40;
}
return forger;
}),
],
{
[CONSENSUS_STATE_DELEGATE_FORGERS_LIST]: codec.encode(
forgerListSchema,
forgersList,
),
[CONSENSUS_STATE_DELEGATE_VOTE_WEIGHTS]: codec.encode(
voteWeightsSchema,
delegateVoteWeights,
),
},
{ lastBlockHeaders: [lastBlock] },
);
// Act
await dpos.apply(block, stateStore);

const updatedMissedForger = await stateStore.account.get(
missedDelegate.address,
);
expect(updatedMissedForger.asset.delegate.isBanned).toBeFalse();
expect(
updatedMissedForger.asset.delegate.consecutiveMissedBlocks,
).toEqual(41);
});
});

describe('when forger missed a block has 50 consecutive missed block, and not forged within 260k blocks', () => {
it('should ban the missed forger', async () => {
const forgedDelegate = forgedDelegates[forgedDelegates.length - 1];
// Arrange
const lastBlock = {
generatorPublicKey: forgedDelegate.publicKey,
height: 920006,
timestamp: 10000270,
} as BlockHeader;
block = {
height: 920007,
timestamp: 10000290,
generatorPublicKey: forgedDelegate.publicKey,
} as BlockHeader;
const forgerIndex = forgersList.forgersList[0].delegates.findIndex(
forger => forger.equals(forgedDelegate.address),
);
const missedDelegate = forgedDelegates[forgerIndex - 1];
forgersList = {
forgersList: [
{
round: 8933,
delegates: [...forgedDelegates.map(d => d.address)],
standby: [],
},
],
};
delegateVoteWeights = {
voteWeights: [
{
round: 10,
delegates: forgedDelegates.map(d => ({
address: d.address,
voteWeight: d.asset.delegate.totalVotesReceived,
})),
},
],
};
stateStore = new StateStoreMock(
[
...forgedDelegates.map(forger => {
if (forger.address.equals(missedDelegate.address)) {
// eslint-disable-next-line no-param-reassign
forger.asset.delegate.lastForgedHeight =
block.height - 260000 - 1;
// eslint-disable-next-line no-param-reassign
forger.asset.delegate.consecutiveMissedBlocks = 50;
}
return forger;
}),
],
{
[CONSENSUS_STATE_DELEGATE_FORGERS_LIST]: codec.encode(
forgerListSchema,
forgersList,
),
[CONSENSUS_STATE_DELEGATE_VOTE_WEIGHTS]: codec.encode(
voteWeightsSchema,
delegateVoteWeights,
),
},
{ lastBlockHeaders: [lastBlock] },
);
// Act
await dpos.apply(block, stateStore);

const updatedMissedForger = await stateStore.account.get(
missedDelegate.address,
);
expect(updatedMissedForger.asset.delegate.isBanned).toBeTrue();
expect(
updatedMissedForger.asset.delegate.consecutiveMissedBlocks,
).toEqual(51);
});
});
});
});

Expand Down

0 comments on commit ca9ec5b

Please sign in to comment.