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

Commit

Permalink
POM Plugin contradicting header validation to the plugin (#8861)
Browse files Browse the repository at this point in the history
* ♻️ Adds contradicting header validation to the plugin instead of invoking chain_areHeadersContradicting endpoint

* ♻️ ✅

* 🚚 ♻️ ✅ get_contradicting_block_header.spec.ts to db.spec.ts

---------

Co-authored-by: !shan <ishantiw.quasar@gmail.com>
  • Loading branch information
has5aan and ishantiw authored Aug 18, 2023
1 parent b2bf341 commit 0c82d87
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 29 deletions.
5 changes: 4 additions & 1 deletion examples/pos-mainchain/src/app/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { ReportMisbehaviorPlugin } from '@liskhq/lisk-framework-report-misbehavior-plugin';
import { Application } from 'lisk-sdk';

export const registerPlugins = (_app: Application): void => {};
export const registerPlugins = (_app: Application): void => {
_app.registerPlugin(new ReportMisbehaviorPlugin());
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
import * as os from 'os';
import { join } from 'path';
import { ensureDir } from 'fs-extra';
import { cryptography, codec, chain, db as liskDB, BasePlugin } from 'lisk-sdk';
import {
cryptography,
codec,
chain,
db as liskDB,
areDistinctHeadersContradicting,
} from 'lisk-sdk';

const { BlockHeader } = chain;
const { utils } = cryptography;
Expand Down Expand Up @@ -94,9 +100,7 @@ type IteratableStream = NodeJS.ReadableStream & { destroy: (err?: Error) => void
export const getContradictingBlockHeader = async (
db: liskDB.Database,
blockHeader: chain.BlockHeader,
apiClient: BasePlugin['apiClient'],
): Promise<chain.BlockHeader | undefined> => {
const header1 = blockHeader.getBytes().toString('hex');
const existingHeaders = await new Promise<string[]>((resolve, reject) => {
const stream = db.createReadStream({
gte: Buffer.concat([blockHeader.generatorAddress, Buffer.alloc(4, 0)]),
Expand All @@ -117,13 +121,18 @@ export const getContradictingBlockHeader = async (
resolve(results);
});
});
for (const header2 of existingHeaders) {
const { valid } = await apiClient.invoke<{ valid: boolean }>('chain_areHeadersContradicting', {
header1,
header2,
});
if (valid) {
return BlockHeader.fromBytes(Buffer.from(header2, 'hex'));

for (const headerString of existingHeaders) {
const header = BlockHeader.fromBytes(Buffer.from(headerString, 'hex'));

if (blockHeader.id.equals(header.id)) {
continue;
}

const isContradicting = areDistinctHeadersContradicting(header, blockHeader);

if (isContradicting) {
return header;
}
}
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export class ReportMisbehaviorPlugin extends BasePlugin<ReportMisbehaviorPluginC
const contradictingBlock = await getContradictingBlockHeader(
this._pluginDB,
decodedBlockHeader,
this.apiClient,
);
if (contradictingBlock && this._state.privateKey) {
const encodedTransaction = await this._createPoMTransaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,31 +83,19 @@ describe('db', () => {
),
id: Buffer.from('e734e77355e887660b5a8382eba26fa0b536b7deb1ddfdda394bdb6614b4cd55', 'hex'),
});
const result = await getContradictingBlockHeader(
db,
header,
reportMisbehaviorPlugin['apiClient'],
);
const result = await getContradictingBlockHeader(db, header);
expect(result).toBeUndefined();
});

it('should resolve undefine when there are no conflicting block header by the same generator', async () => {
const decodedBlockHeader1 = chain.BlockHeader.fromBytes(blockHeader1);
const result = await getContradictingBlockHeader(
db,
decodedBlockHeader1,
reportMisbehaviorPlugin['apiClient'],
);
const result = await getContradictingBlockHeader(db, decodedBlockHeader1);
expect(result).toBeUndefined();
});

it('should resolve a contradicting blockheader when there is a conflicting block in the database', async () => {
const decodedBlockHeader1 = chain.BlockHeader.fromBytes(blockHeader1Contradicting);
const result = await getContradictingBlockHeader(
db,
decodedBlockHeader1,
reportMisbehaviorPlugin['apiClient'],
);
const result = await getContradictingBlockHeader(db, decodedBlockHeader1);
expect(result).toBeDefined();

expect(result?.getBytes().toString('hex')).toEqual(blockHeader1.toString('hex'));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright © 2023 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

import { rmSync } from 'fs-extra';
import * as sdk from 'lisk-sdk';
import { chain, db as liskDB, blockHeaderSchema, codec, BlockHeader } from 'lisk-sdk';
import { getContradictingBlockHeader, saveBlockHeaders } from '../../src/db';

describe('db', () => {
const dbPath = 'test-lisk-framework-report-misbehavior-plugin.db';
const db = new liskDB.Database(dbPath);

const random20Bytes = Buffer.from('40ff452fae2affe6eeef3c30e53e9eac35a1bc43', 'hex');
const random32Bytes = Buffer.from(
'3d1b5dd1ef4ff7b22359598ebdf58966a51adcc03e02ad356632743e65898990',
'hex',
);
const header1 = new chain.BlockHeader({
height: 100,
aggregateCommit: {
aggregationBits: Buffer.alloc(0),
certificateSignature: Buffer.alloc(0),
height: 0,
},
impliesMaxPrevotes: false,
generatorAddress: random20Bytes,
maxHeightGenerated: 0,
maxHeightPrevoted: 50,
previousBlockID: random32Bytes,
timestamp: 100,
version: 2,
assetRoot: random32Bytes,
eventRoot: random32Bytes,
stateRoot: random32Bytes,
transactionRoot: random32Bytes,
validatorsHash: random32Bytes,
signature: random32Bytes,
});

const headerBytes1 = codec.encode(blockHeaderSchema, header1);

afterAll(async () => {
await db.clear();

db.close();

rmSync(dbPath, {
recursive: true,
force: true,
});
});

describe('getContradictingBlockHeader', () => {
const headerClone = BlockHeader.fromBytes(headerBytes1);

const header2 = new chain.BlockHeader({
id: Buffer.from('ff', 'hex'),
height: 100,
aggregateCommit: {
aggregationBits: Buffer.alloc(0),
certificateSignature: Buffer.alloc(0),
height: 0,
},
impliesMaxPrevotes: false,
generatorAddress: random20Bytes,
maxHeightGenerated: 0,
maxHeightPrevoted: 32,
previousBlockID: random32Bytes,
timestamp: 100,
version: 2,
assetRoot: random32Bytes,
eventRoot: random32Bytes,
stateRoot: random32Bytes,
transactionRoot: random32Bytes,
validatorsHash: random32Bytes,
signature: random32Bytes,
});

beforeEach(async () => {
await saveBlockHeaders(db, headerBytes1);
});

it('should return undefined if plugin database is empty', async () => {
await expect(getContradictingBlockHeader(db, headerClone)).resolves.toBeUndefined();
});

it('should return undefined if block headers have same id', async () => {
await expect(getContradictingBlockHeader(db, headerClone)).resolves.toBeUndefined();
});

it('should return undefined if block headers are not contradicting', async () => {
jest.spyOn(sdk, 'areDistinctHeadersContradicting').mockImplementation(() => false);

await expect(getContradictingBlockHeader(db, header2)).resolves.toBeUndefined();
});

it('should return the block in plugin db if blocks are contradicting', async () => {
jest.spyOn(sdk, 'areDistinctHeadersContradicting').mockImplementation(() => true);

await expect(getContradictingBlockHeader(db, header2)).resolves.toEqual(headerClone);
});
});
});
2 changes: 1 addition & 1 deletion framework/src/engine/bft/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@

export { BFTModule } from './module';
export type { BFTMethod } from './method';
export { computeValidatorsHash } from './utils';
export { computeValidatorsHash, areDistinctHeadersContradicting } from './utils';
export { BFTParameters } from './schemas';
2 changes: 1 addition & 1 deletion framework/src/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
*/

export { Engine } from './engine';
export { computeValidatorsHash, BFTParameters } from './bft';
export { computeValidatorsHash, areDistinctHeadersContradicting, BFTParameters } from './bft';
1 change: 1 addition & 0 deletions framework/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,4 @@ export {
CHAIN_ID_LENGTH,
} from './modules/interoperability/constants';
export { Proof, QueryProof, ProveResponse, Validator as BFTValidator } from './abi/abi';
export { areDistinctHeadersContradicting } from './engine/bft';

0 comments on commit 0c82d87

Please sign in to comment.