From 795aafebd0ab6f2704ea07fde3bbc402adde9b37 Mon Sep 17 00:00:00 2001 From: shuse2 Date: Mon, 18 Oct 2021 14:39:22 +0200 Subject: [PATCH] :recycle: Update plugin to follow new application architecture (#6835) --- elements/lisk-elements/src/index.ts | 1 - .../ui/components/dialogs/NodeInfoDialog.tsx | 2 +- .../widgets/SendTransactionWidget.tsx | 18 +-- .../components/widgets/TransactionWidget.tsx | 6 +- .../src/ui/pages/MainPage.tsx | 28 ++-- .../src/ui/types.ts | 15 +- .../src/plugin/endpoint.ts | 123 ++++++++++++++++ .../src/plugin/faucet_plugin.ts | 116 ++------------- .../src/actions/forging_info.ts | 62 -------- .../src/actions/index.ts | 18 --- .../src/actions/voters.ts | 61 -------- .../src/endpoint.ts | 124 ++++++++++++++++ .../src/forger_plugin.ts | 136 ++++++++---------- .../src/schemas.ts | 30 ++++ .../lisk-framework-forger-plugin/src/types.ts | 2 +- .../src/controllers/blocks.ts | 8 +- .../src/controllers/forks.ts | 2 +- .../src/controllers/network.ts | 12 +- .../src/controllers/prometheus.ts | 14 +- .../src/controllers/transactions.ts | 8 +- .../src/endpoint.ts | 49 +++++++ .../src/monitor_plugin.ts | 67 +++------ .../src/db.ts | 70 ++++----- .../src/endpoint.ts | 65 +++++++++ .../src/report_misbehavior_plugin.ts | 86 +++-------- framework/src/index.ts | 2 +- framework/src/modules/bft/endpoint.ts | 23 ++- framework/src/modules/bft/schemas.ts | 15 ++ framework/src/plugins/base_plugin.ts | 4 +- package.json | 4 +- 30 files changed, 630 insertions(+), 541 deletions(-) create mode 100644 framework-plugins/lisk-framework-faucet-plugin/src/plugin/endpoint.ts delete mode 100644 framework-plugins/lisk-framework-forger-plugin/src/actions/forging_info.ts delete mode 100644 framework-plugins/lisk-framework-forger-plugin/src/actions/index.ts delete mode 100644 framework-plugins/lisk-framework-forger-plugin/src/actions/voters.ts create mode 100644 framework-plugins/lisk-framework-forger-plugin/src/endpoint.ts create mode 100644 framework-plugins/lisk-framework-monitor-plugin/src/endpoint.ts create mode 100644 framework-plugins/lisk-framework-report-misbehavior-plugin/src/endpoint.ts diff --git a/elements/lisk-elements/src/index.ts b/elements/lisk-elements/src/index.ts index 29655b744e4..d3d7015b84b 100644 --- a/elements/lisk-elements/src/index.ts +++ b/elements/lisk-elements/src/index.ts @@ -24,4 +24,3 @@ export * as validator from '@liskhq/lisk-validator'; export * as codec from '@liskhq/lisk-codec'; export * as db from '@liskhq/lisk-db'; export * as chain from '@liskhq/lisk-chain'; -export * as genesis from '@liskhq/lisk-genesis'; diff --git a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/dialogs/NodeInfoDialog.tsx b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/dialogs/NodeInfoDialog.tsx index a3fffc1a860..16fccc49790 100644 --- a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/dialogs/NodeInfoDialog.tsx +++ b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/dialogs/NodeInfoDialog.tsx @@ -105,7 +105,7 @@ const NodeInfoDialog: React.FC = props => { {fee.moduleID} - {fee.assetID} + {fee.commandID} {fee.baseFee} diff --git a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/SendTransactionWidget.tsx b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/SendTransactionWidget.tsx index 234fe35cf5d..bfd0aca5bdb 100644 --- a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/SendTransactionWidget.tsx +++ b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/SendTransactionWidget.tsx @@ -31,7 +31,7 @@ const SendTransactionWidget: React.FC = props => { const [validAsset, setValidAsset] = React.useState(true); const assets = props.modules .map(m => - m.transactionAssets.map(t => ({ + m.commands.map(t => ({ label: `${m.name}:${t.name}`, value: `${m.name}:${t.name}`, })), @@ -42,17 +42,17 @@ const SendTransactionWidget: React.FC = props => { const handleSubmit = () => { const assetSelectedValue = selectedAsset ? selectedAsset.value : ''; const moduleName = assetSelectedValue.split(':').shift(); - const assetName = assetSelectedValue.split(':').slice(1).join(':'); + const commandName = assetSelectedValue.split(':').slice(1).join(':'); let moduleID: number | undefined; - let assetID: number | undefined; + let commandID: number | undefined; for (const m of props.modules) { if (m.name === moduleName) { moduleID = m.id; - for (const t of m.transactionAssets) { - if (t.name === assetName) { - assetID = t.id; + for (const t of m.commands) { + if (t.name === commandName) { + commandID = t.id; break; } @@ -62,12 +62,12 @@ const SendTransactionWidget: React.FC = props => { } } - if (moduleID !== undefined && assetID !== undefined) { + if (moduleID !== undefined && commandID !== undefined) { props.onSubmit({ moduleID, - assetID, + commandID, passphrase, - asset: JSON.parse(asset) as Record, + params: JSON.parse(asset) as Record, }); } }; diff --git a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/TransactionWidget.tsx b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/TransactionWidget.tsx index e8435bb7ef9..4f2dd523c33 100644 --- a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/TransactionWidget.tsx +++ b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/components/widgets/TransactionWidget.tsx @@ -28,7 +28,7 @@ interface WidgetProps { const getModuleAsset = ( nodeInfo: NodeInfo | undefined, moduleID: number, - assetID: number, + commandID: number, ): string => { if (!nodeInfo) { return 'unknown'; @@ -37,7 +37,7 @@ const getModuleAsset = ( if (!registeredModule) { return 'unknown'; } - const registeredAsset = registeredModule.transactionAssets?.find(ta => ta.id === assetID); + const registeredAsset = registeredModule.commands?.find(ta => ta.id === commandID); if (!registeredAsset) { return `${registeredModule.name}:unknown`; } @@ -83,7 +83,7 @@ const TransactionWidget: React.FC = props => { - {getModuleAsset(props.nodeInfo, transaction.moduleID, transaction.assetID)} + {getModuleAsset(props.nodeInfo, transaction.moduleID, transaction.commandID)} diff --git a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/pages/MainPage.tsx b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/pages/MainPage.tsx index 60b2fc2d570..74357330135 100644 --- a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/pages/MainPage.tsx +++ b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/pages/MainPage.tsx @@ -85,16 +85,6 @@ const callAndProcessActions = async ( let result = (await client.invoke(action, params)) as unknown; switch (action) { - case 'app:getAccount': - result = client.account.toJSON(client.account.decode(result as string)); - break; - - case 'app:getAccounts': - result = (result as string[]).map(account => - client.account.toJSON(client.account.decode(account)), - ); - break; - case 'app:getLastBlock': case 'app:getBlockByID': case 'app:getBlockByHeight': @@ -289,29 +279,29 @@ const MainPage: React.FC = () => { const { publicKey, address } = cryptography.getAddressAndPublicKeyFromPassphrase( data.passphrase, ); - const assetSchema = getClient().schemas.transactionsAssets.find( - a => a.moduleID === data.moduleID && a.assetID === data.assetID, + const assetSchema = getClient().schemas.commands.find( + a => a.moduleID === data.moduleID && a.commandID === data.commandID, ); if (!assetSchema) { - throw new Error(`ModuleID: ${data.moduleID} AssetID: ${data.assetID} is not registered`); + throw new Error(`ModuleID: ${data.moduleID} AssetID: ${data.commandID} is not registered`); } const assetObject = codec.codec.fromJSON>( assetSchema.schema, - data.asset, + data.params, ); - const sender = await getClient().account.get(address); + const sender = await getClient().invoke<{ nonce: string }>('auth_getAuthData', { address: address.toString('hex') }); const fee = getClient().transaction.computeMinFee({ moduleID: data.moduleID, - assetID: data.assetID, + commandID: data.commandID, asset: assetObject, senderPublicKey: publicKey, - nonce: BigInt((sender.sequence as { nonce: bigint }).nonce), + nonce: BigInt(sender.nonce), }); const transaction = await getClient().transaction.create( { moduleID: data.moduleID, - assetID: data.assetID, - asset: assetObject, + commandID: data.commandID, + params: assetObject, senderPublicKey: publicKey, fee, }, diff --git a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/types.ts b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/types.ts index 49cf47349df..d98f1f34530 100644 --- a/framework-plugins/lisk-framework-dashboard-plugin/src/ui/types.ts +++ b/framework-plugins/lisk-framework-dashboard-plugin/src/ui/types.ts @@ -33,7 +33,7 @@ export interface GenesisConfig { readonly minFeePerByte: number; readonly baseFees: { readonly moduleID: number; - readonly assetID: number; + readonly commandID: number; readonly baseFee: string; }[]; } @@ -53,17 +53,16 @@ export interface NodeInfo { export interface Fee { moduleId: number; - assetId: number; + commandId: number; baseFee: number; } export interface RegisteredModule { id: number; name: string; - actions: string[]; events: string[]; - reducers: string[]; - transactionAssets: { + actions: string[]; + commands: { id: number; name: string; }[]; @@ -84,7 +83,7 @@ export interface Transaction { id: string; senderPublicKey: string; moduleID: number; - assetID: number; + commandID: number; fee: number; } @@ -95,8 +94,8 @@ export interface EventData { export interface SendTransactionOptions { moduleID: number; - assetID: number; - asset: Record; + commandID: number; + params: Record; passphrase: string; } diff --git a/framework-plugins/lisk-framework-faucet-plugin/src/plugin/endpoint.ts b/framework-plugins/lisk-framework-faucet-plugin/src/plugin/endpoint.ts new file mode 100644 index 00000000000..5e9d94052f1 --- /dev/null +++ b/framework-plugins/lisk-framework-faucet-plugin/src/plugin/endpoint.ts @@ -0,0 +1,123 @@ +/* + * Copyright © 2021 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 axios from 'axios'; +import { + decryptPassphraseWithPassword, + getAddressAndPublicKeyFromPassphrase, + parseEncryptedPassphrase, +} from '@liskhq/lisk-cryptography'; +import { LiskValidationError, validator } from '@liskhq/lisk-validator'; +import { BasePluginEndpoint, PluginEndpointContext } from 'lisk-framework'; +import { convertLSKToBeddows } from '@liskhq/lisk-transactions'; +import { APIClient } from '@liskhq/lisk-api-client'; +import { authorizeParamsSchema, fundParamsSchema } from './schemas'; +import { FaucetPluginConfig, State } from './types'; + +export class Endpoint extends BasePluginEndpoint { + private _state: State = { publicKey: undefined, passphrase: undefined }; + private _client!: APIClient; + private _config!: FaucetPluginConfig; + + public init(state: State, apiClient: APIClient, config: FaucetPluginConfig) { + this._state = state; + this._client = apiClient; + this._config = config; + } + // eslint-disable-next-line @typescript-eslint/require-await + public async authorize(context: PluginEndpointContext): Promise<{ result: string }> { + const errors = validator.validate(authorizeParamsSchema, context.params); + + if (errors.length) { + throw new LiskValidationError(errors); + } + + const { enable, password } = context.params; + + try { + const parsedEncryptedPassphrase = parseEncryptedPassphrase(this._config.encryptedPassphrase); + + const passphrase = decryptPassphraseWithPassword( + parsedEncryptedPassphrase, + password as string, + ); + + const { publicKey } = getAddressAndPublicKeyFromPassphrase(passphrase); + + this._state.publicKey = enable ? publicKey : undefined; + this._state.passphrase = enable ? passphrase : undefined; + const changedState = enable ? 'enabled' : 'disabled'; + + return { + result: `Successfully ${changedState} the faucet.`, + }; + } catch (error) { + throw new Error('Password given is not valid.'); + } + } + + public async fundTokens(context: PluginEndpointContext): Promise<{ result: string }> { + const errors = validator.validate(fundParamsSchema, context.params); + const { address, token } = context.params; + + if (errors.length) { + throw new LiskValidationError(errors); + } + + if (!this._state.publicKey || !this._state.passphrase) { + throw new Error('Faucet is not enabled.'); + } + + const captchaResult = await axios({ + method: 'post', + url: 'https://www.google.com/recaptcha/api/siteverify', + params: { + secret: this._config.captchaSecretkey, + response: token, + }, + }); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (!captchaResult?.data?.success) { + throw new Error('Captcha response was invalid.'); + } + + await this._transferFunds(address as string); + + return { + result: `Successfully funded account at address: ${address as string}.`, + }; + } + + private async _transferFunds(address: string): Promise { + const transferTransactionParams = { + amount: BigInt(convertLSKToBeddows(this._config.amount)), + recipientAddress: Buffer.from(address, 'hex'), + data: '', + }; + + const transaction = await this._client.transaction.create( + { + moduleID: 2, + commandID: 0, + senderPublicKey: this._state.publicKey as Buffer, + fee: BigInt(convertLSKToBeddows(this._config.fee)), // TODO: The static fee should be replaced by fee estimation calculation + params: transferTransactionParams, + }, + this._state.passphrase as string, + ); + + await this._client.transaction.send(transaction); + } +} diff --git a/framework-plugins/lisk-framework-faucet-plugin/src/plugin/faucet_plugin.ts b/framework-plugins/lisk-framework-faucet-plugin/src/plugin/faucet_plugin.ts index 15e642992e5..22acc162aad 100644 --- a/framework-plugins/lisk-framework-faucet-plugin/src/plugin/faucet_plugin.ts +++ b/framework-plugins/lisk-framework-faucet-plugin/src/plugin/faucet_plugin.ts @@ -11,30 +11,20 @@ * * Removal or modification of this copyright notice is prohibited. */ - -import { APIClient, createClient } from '@liskhq/lisk-api-client'; -import { validator, LiskValidationError } from '@liskhq/lisk-validator'; -import { convertLSKToBeddows } from '@liskhq/lisk-transactions'; -import { - decryptPassphraseWithPassword, - parseEncryptedPassphrase, - getAddressAndPublicKeyFromPassphrase, - getLisk32AddressFromPublicKey, -} from '@liskhq/lisk-cryptography'; -import axios from 'axios'; -import { ActionsDefinition, BasePlugin, BaseChannel } from 'lisk-framework'; +import { getLisk32AddressFromPublicKey } from '@liskhq/lisk-cryptography'; +import { BasePlugin, PluginInitContext } from 'lisk-framework'; import * as express from 'express'; import { join } from 'path'; import { Server } from 'http'; -import { configSchema, authorizeParamsSchema, fundParamsSchema } from './schemas'; +import { configSchema } from './schemas'; import { FaucetPluginConfig, State } from './types'; +import { Endpoint } from './endpoint'; export class FaucetPlugin extends BasePlugin { public name = 'faucet'; public configSchema = configSchema; + public endpoint = new Endpoint(); - private _channel!: BaseChannel; - private _client!: APIClient; private _server!: Server; private readonly _state: State = { publicKey: undefined, passphrase: undefined }; @@ -42,80 +32,13 @@ export class FaucetPlugin extends BasePlugin { return __filename; } - public get actions(): ActionsDefinition { - return { - authorize: (params?: Record): { result: string } => { - const errors = validator.validate(authorizeParamsSchema, params as Record); - - if (errors.length) { - throw new LiskValidationError([...errors]); - } - - const { enable, password } = params as Record; - - try { - const parsedEncryptedPassphrase = parseEncryptedPassphrase( - this.config.encryptedPassphrase, - ); - - const passphrase = decryptPassphraseWithPassword( - parsedEncryptedPassphrase, - password as string, - ); - - const { publicKey } = getAddressAndPublicKeyFromPassphrase(passphrase); - - this._state.publicKey = enable ? publicKey : undefined; - this._state.passphrase = enable ? passphrase : undefined; - const changedState = enable ? 'enabled' : 'disabled'; - - return { - result: `Successfully ${changedState} the faucet.`, - }; - } catch (error) { - throw new Error('Password given is not valid.'); - } - }, - fundTokens: async (params?: Record): Promise<{ result: string }> => { - const errors = validator.validate(fundParamsSchema, params as Record); - const { address, token } = params as Record; - - if (errors.length) { - throw new LiskValidationError([...errors]); - } - - if (!this._state.publicKey || !this._state.passphrase) { - throw new Error('Faucet is not enabled.'); - } - - const captchaResult = await axios({ - method: 'post', - url: 'https://www.google.com/recaptcha/api/siteverify', - params: { - secret: this.config.captchaSecretkey, - response: token, - }, - }); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!captchaResult?.data?.success) { - throw new Error('Captcha response was invalid.'); - } - - await this._transferFunds(address as string); - - return { - result: `Successfully funded account at address: ${address as string}.`, - }; - }, - }; + public async init(context: PluginInitContext): Promise { + await super.init(context); + this.endpoint.init(this._state, this.apiClient, this.config); } // eslint-disable-next-line @typescript-eslint/require-await, class-methods-use-this - public async load(channel: BaseChannel): Promise { - this._channel = channel; - this._client = await createClient(this._channel); - + public async load(): Promise { const app = express(); app.get('/api/config', (_req, res) => { const config = { @@ -145,25 +68,4 @@ export class FaucetPlugin extends BasePlugin { }); }); } - - private async _transferFunds(address: string): Promise { - const transferTransactionAsset = { - amount: BigInt(convertLSKToBeddows(this.config.amount)), - recipientAddress: Buffer.from(address, 'hex'), - data: '', - }; - - const transaction = await this._client.transaction.create( - { - moduleID: 2, - assetID: 0, - senderPublicKey: this._state.publicKey as Buffer, - fee: BigInt(convertLSKToBeddows(this.config.fee)), // TODO: The static fee should be replaced by fee estimation calculation - asset: transferTransactionAsset, - }, - this._state.passphrase as string, - ); - - await this._client.transaction.send(transaction); - } } diff --git a/framework-plugins/lisk-framework-forger-plugin/src/actions/forging_info.ts b/framework-plugins/lisk-framework-forger-plugin/src/actions/forging_info.ts deleted file mode 100644 index 84713cfa9bb..00000000000 --- a/framework-plugins/lisk-framework-forger-plugin/src/actions/forging_info.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2020 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 { BaseChannel, PluginCodec } from 'lisk-framework'; -import { KVStore } from '@liskhq/lisk-db'; -import { getForgerInfo } from '../db'; -import { Forger, DPoSAccountJSON } from '../types'; - -interface ForgerInfo extends Forger { - readonly username: string; - readonly totalReceivedFees: string; - readonly totalReceivedRewards: string; - readonly totalProducedBlocks: number; - readonly totalVotesReceived: string; - readonly consecutiveMissedBlocks: number; -} - -export const getForgingInfo = async ( - channel: BaseChannel, - codec: PluginCodec, - db: KVStore, -): Promise => { - const forgingDelegates = await channel.invoke>('app:getForgingStatus'); - const encodedAccounts = await channel.invoke('app:getAccounts', { - address: forgingDelegates.map(forger => forger.address), - }); - const forgerAccounts = encodedAccounts.map(encodedAccount => - codec.decodeAccount(encodedAccount), - ); - - const data: ForgerInfo[] = []; - for (const forgerAccount of forgerAccounts) { - const forgerAddressBinary = Buffer.from(forgerAccount.address, 'hex').toString('binary'); - const forgerInfo = await getForgerInfo(db, forgerAddressBinary); - const forger = forgingDelegates.find(aForger => aForger.address === forgerAccount.address); - - if (forger) { - data.push({ - ...forger, - username: forgerAccount.dpos.delegate.username, - totalReceivedFees: forgerInfo.totalReceivedFees.toString(), - totalReceivedRewards: forgerInfo.totalReceivedRewards.toString(), - totalProducedBlocks: forgerInfo.totalProducedBlocks, - totalVotesReceived: forgerAccount.dpos.delegate.totalVotesReceived, - consecutiveMissedBlocks: forgerAccount.dpos.delegate.consecutiveMissedBlocks, - }); - } - } - - return data; -}; diff --git a/framework-plugins/lisk-framework-forger-plugin/src/actions/index.ts b/framework-plugins/lisk-framework-forger-plugin/src/actions/index.ts deleted file mode 100644 index 93a5777293a..00000000000 --- a/framework-plugins/lisk-framework-forger-plugin/src/actions/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright © 2020 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 * as voters from './voters'; -import * as forgingInfo from './forging_info'; - -export { voters, forgingInfo }; diff --git a/framework-plugins/lisk-framework-forger-plugin/src/actions/voters.ts b/framework-plugins/lisk-framework-forger-plugin/src/actions/voters.ts deleted file mode 100644 index 05faaa2c52b..00000000000 --- a/framework-plugins/lisk-framework-forger-plugin/src/actions/voters.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2020 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 { BaseChannel, PluginCodec } from 'lisk-framework'; -import { KVStore } from '@liskhq/lisk-db'; -import { Forger, DPoSAccountJSON } from '../types'; -import { getForgerInfo } from '../db'; - -interface Voter { - readonly address: string; - readonly username: string; - readonly totalVotesReceived: string; - readonly voters: { - readonly address: string; - readonly amount: string; - }[]; -} - -export const getVoters = async ( - channel: BaseChannel, - codec: PluginCodec, - db: KVStore, -): Promise => { - const forgersList = await channel.invoke('app:getForgingStatus'); - const forgerAccounts = ( - await channel.invoke('app:getAccounts', { - address: forgersList.map(forger => forger.address), - }) - ).map(encodedAccount => codec.decodeAccount(encodedAccount)); - - const result: Voter[] = []; - for (const account of forgerAccounts) { - const forgerInfo = await getForgerInfo( - db, - Buffer.from(account.address, 'hex').toString('binary'), - ); - - result.push({ - address: account.address, - username: account.dpos.delegate.username, - totalVotesReceived: account.dpos.delegate.totalVotesReceived, - voters: forgerInfo.votesReceived.map(vote => ({ - address: vote.address.toString('hex'), - amount: vote.amount.toString(), - })), - }); - } - - return result; -}; diff --git a/framework-plugins/lisk-framework-forger-plugin/src/endpoint.ts b/framework-plugins/lisk-framework-forger-plugin/src/endpoint.ts new file mode 100644 index 00000000000..139b29250c3 --- /dev/null +++ b/framework-plugins/lisk-framework-forger-plugin/src/endpoint.ts @@ -0,0 +1,124 @@ +/* + * Copyright © 2021 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 { KVStore } from '@liskhq/lisk-db'; +import { BasePlugin, BasePluginEndpoint, PluginEndpointContext } from 'lisk-framework'; +import { getForgerInfo } from './db'; +import { Forger } from './types'; + +interface Voter { + readonly address: string; + readonly username: string; + readonly totalVotesReceived: string; + readonly voters: { + readonly address: string; + readonly amount: string; + }[]; +} + +interface ForgerInfo extends Forger { + readonly username: string; + readonly totalReceivedFees: string; + readonly totalReceivedRewards: string; + readonly totalProducedBlocks: number; + readonly totalVotesReceived: string; + readonly consecutiveMissedBlocks: number; +} + +interface Delegate { + name: string; + totalVotesReceived: string; + selfVotes: string; + lastGeneratedHeight: number; + isBanned: boolean; + pomHeights: ReadonlyArray; + consecutiveMissedBlocks: number; +} + +export class Endpoint extends BasePluginEndpoint { + private _client!: BasePlugin['apiClient']; + private _db!: KVStore; + + public init(db: KVStore, apiClient: BasePlugin['apiClient']) { + this._db = db; + this._client = apiClient; + } + + public async getVoters(_context: PluginEndpointContext): Promise { + const forgersList = await this._client.invoke('app_getForgingStatus'); + const forgerAccounts = []; + for (const delegate of forgersList) { + const res = await this._client.invoke('dpos_getDelegate', { + address: delegate.address, + }); + forgerAccounts.push({ + ...res, + address: delegate.address, + }); + } + + const result: Voter[] = []; + for (const account of forgerAccounts) { + const forgerInfo = await getForgerInfo( + this._db, + Buffer.from(account.address, 'hex').toString('binary'), + ); + + result.push({ + address: account.address, + username: account.name, + totalVotesReceived: account.totalVotesReceived, + voters: forgerInfo.votesReceived.map(vote => ({ + address: vote.address.toString('hex'), + amount: vote.amount.toString(), + })), + }); + } + + return result; + } + + public async getForgingInfo(_context: PluginEndpointContext): Promise { + const forgersList = await this._client.invoke>('app_getForgingStatus'); + const forgerAccounts = []; + for (const delegate of forgersList) { + const res = await this._client.invoke('dpos_getDelegate', { + address: delegate.address, + }); + forgerAccounts.push({ + ...res, + address: delegate.address, + }); + } + const data: ForgerInfo[] = []; + for (const forgerAccount of forgerAccounts) { + const forgerAddressBinary = Buffer.from(forgerAccount.address, 'hex').toString('binary'); + const forgerInfo = await getForgerInfo(this._db, forgerAddressBinary); + const forger = forgersList.find(aForger => aForger.address === forgerAccount.address); + + if (forger) { + data.push({ + ...forger, + username: forgerAccount.name, + totalReceivedFees: forgerInfo.totalReceivedFees.toString(), + totalReceivedRewards: forgerInfo.totalReceivedRewards.toString(), + totalProducedBlocks: forgerInfo.totalProducedBlocks, + totalVotesReceived: forgerAccount.totalVotesReceived, + consecutiveMissedBlocks: forgerAccount.consecutiveMissedBlocks, + }); + } + } + + return data; + } +} diff --git a/framework-plugins/lisk-framework-forger-plugin/src/forger_plugin.ts b/framework-plugins/lisk-framework-forger-plugin/src/forger_plugin.ts index 0459d47d4bb..22c2ee08194 100644 --- a/framework-plugins/lisk-framework-forger-plugin/src/forger_plugin.ts +++ b/framework-plugins/lisk-framework-forger-plugin/src/forger_plugin.ts @@ -12,19 +12,11 @@ * Removal or modification of this copyright notice is prohibited. */ -import { getAddressFromPublicKey } from '@liskhq/lisk-cryptography'; import { KVStore } from '@liskhq/lisk-db'; -import { - ActionsDefinition, - BasePlugin, - BaseChannel, - EventsDefinition, - TransactionJSON, - BlockHeaderJSON, - GenesisConfig, -} from 'lisk-framework'; +import { BasePlugin, BaseChannel, GenesisConfig } from 'lisk-framework'; import { dataStructures } from '@liskhq/lisk-utils'; - +import { Block, BlockHeader, blockSchema, Transaction } from '@liskhq/lisk-chain'; +import { codec } from '@liskhq/lisk-codec'; import { getDBInstance, getForgerInfo, @@ -33,15 +25,17 @@ import { setForgerSyncInfo, } from './db'; import { Forger, TransactionFees, Voters } from './types'; -import * as actions from './actions'; +import { Endpoint } from './endpoint'; const BLOCKS_BATCH_TO_SYNC = 1000; +const MODULE_ID_DPOS = 5; +const COMMAND_ID_VOTE = 1; interface Data { readonly block: string; } -interface Asset { +interface VotesParams { readonly votes: Array>; } interface Vote { @@ -53,8 +47,8 @@ interface ForgerPayloadInfo { forgerAddress: string; forgerAddressBuffer: Buffer; forgerAddressBinary: string; - header: BlockHeaderJSON; - payload: readonly TransactionJSON[]; + header: BlockHeader; + payload: readonly Transaction[]; } interface NodeInfo { @@ -76,6 +70,7 @@ const getAddressBuffer = (hexAddressStr: string) => Buffer.from(hexAddressStr, ' export class ForgerPlugin extends BasePlugin { public name = 'forger'; + public endpoint = new Endpoint(); private _forgerPluginDB!: KVStore; private _channel!: BaseChannel; @@ -87,19 +82,10 @@ export class ForgerPlugin extends BasePlugin { return __filename; } - public get events(): EventsDefinition { + public get events(): string[] { return ['block:created', 'block:missed']; } - public get actions(): ActionsDefinition { - return { - getVoters: async () => - actions.voters.getVoters(this._channel, this.codec, this._forgerPluginDB), - getForgingInfo: async () => - actions.forgingInfo.getForgingInfo(this._channel, this.codec, this._forgerPluginDB), - }; - } - // eslint-disable-next-line @typescript-eslint/require-await public async load(channel: BaseChannel): Promise { this._channel = channel; @@ -108,22 +94,19 @@ export class ForgerPlugin extends BasePlugin { // eslint-disable-next-line new-cap this._forgerPluginDB = await getDBInstance(this.dataPath); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - this._channel.once('app:ready', async () => { - // Fetch and set forger list from the app - await this._setForgersList(); + // Fetch and set forger list from the app + await this._setForgersList(); - // Fetch and set transactions fees - await this._setTransactionFees(); + // Fetch and set transactions fees + await this._setTransactionFees(); - // Sync the information - this._syncingWithNode = true; - await this._syncForgerInfo(); - this._syncingWithNode = false; + // Sync the information + this._syncingWithNode = true; + await this._syncForgerInfo(); + this._syncingWithNode = false; - // Listen to new block and delete block events - this._subscribeToChannel(); - }); + // Listen to new block and delete block events + this._subscribeToChannel(); } public async unload(): Promise { @@ -132,25 +115,23 @@ export class ForgerPlugin extends BasePlugin { private async _setForgersList(): Promise { this._forgersList = new dataStructures.BufferMap(); - const forgersList = await this._channel.invoke('app:getForgingStatus'); + const forgersList = await this._channel.invoke('app_getForgingStatus'); for (const { address, forging } of forgersList) { this._forgersList.set(Buffer.from(address, 'hex'), forging); } } private async _setTransactionFees(): Promise { - const { genesisConfig } = await this._channel.invoke('app:getNodeInfo'); + const { genesisConfig } = await this._channel.invoke('app_getNodeInfo'); this._transactionFees = { minFeePerByte: genesisConfig.minFeePerByte, baseFees: genesisConfig.baseFees, }; } - private _getForgerHeaderAndPayloadInfo(block: string): ForgerPayloadInfo { - const { header, payload } = this.codec.decodeBlock(block); - const forgerAddress = getAddressFromPublicKey( - Buffer.from(header.generatorPublicKey, 'hex'), - ).toString('hex'); + private _getForgerHeaderAndPayloadInfo(blockBytes: string): ForgerPayloadInfo { + const block = Block.fromBytes(Buffer.from(blockBytes, 'hex')); + const forgerAddress = block.header.generatorAddress.toString('hex'); const forgerAddressBuffer = getAddressBuffer(forgerAddress); const forgerAddressBinary = getBinaryAddress(forgerAddress); @@ -158,17 +139,18 @@ export class ForgerPlugin extends BasePlugin { forgerAddress, forgerAddressBuffer, forgerAddressBinary, - header, - payload, + header: block.header, + payload: block.payload, }; } private async _syncForgerInfo(): Promise { + const lastBlockBytes = await this._channel.invoke('app_getLastBlock'); const { header: { height: lastBlockHeight }, - } = this.codec.decodeBlock(await this._channel.invoke('app:getLastBlock')); + } = Block.fromBytes(Buffer.from(lastBlockBytes, 'hex')); const { syncUptoHeight } = await getForgerSyncInfo(this._forgerPluginDB); - const { genesisHeight } = await this._channel.invoke('app:getNodeInfo'); + const { genesisHeight } = await this._channel.invoke('app_getNodeInfo'); const forgerPluginSyncedHeight = syncUptoHeight === 0 ? genesisHeight : syncUptoHeight; if (forgerPluginSyncedHeight === lastBlockHeight) { @@ -194,7 +176,7 @@ export class ForgerPlugin extends BasePlugin { ? BLOCKS_BATCH_TO_SYNC : lastBlockHeight - needleHeight); - const blocks = await this._channel.invoke('app:getBlocksByHeightBetween', { + const blocks = await this._channel.invoke('app_getBlocksByHeightBetween', { from: needleHeight, to: toHeight, }); @@ -216,7 +198,7 @@ export class ForgerPlugin extends BasePlugin { private _subscribeToChannel(): void { // eslint-disable-next-line @typescript-eslint/no-misused-promises - this._channel.subscribe('app:block:new', async (data?: Record) => { + this._channel.subscribe('app_block:new', async (data?: Record) => { const { block } = (data as unknown) as Data; const forgerPayloadInfo = this._getForgerHeaderAndPayloadInfo(block); const { @@ -228,7 +210,7 @@ export class ForgerPlugin extends BasePlugin { }); // eslint-disable-next-line @typescript-eslint/no-misused-promises - this._channel.subscribe('app:block:delete', async (data?: Record) => { + this._channel.subscribe('app_block:delete', async (data?: Record) => { const { block } = (data as unknown) as Data; const forgerPayloadInfo = this._getForgerHeaderAndPayloadInfo(block); const { @@ -248,18 +230,16 @@ export class ForgerPlugin extends BasePlugin { forgerAddress, forgerAddressBuffer, forgerAddressBinary, - header: { reward, height }, + header: { height }, payload, } = forgerPayloadInfo; const forgerInfo = await getForgerInfo(this._forgerPluginDB, forgerAddressBinary); if (this._forgersList.has(forgerAddressBuffer)) { forgerInfo.totalProducedBlocks += 1; - forgerInfo.totalReceivedRewards += BigInt(reward); forgerInfo.totalReceivedFees += this._getFee(payload, encodedBlock); this._channel.publish('forger:block:created', { - reward, forgerAddress, height, timestamp: Date.now(), @@ -275,17 +255,11 @@ export class ForgerPlugin extends BasePlugin { encodedBlock: string, forgerPayloadInfo: ForgerPayloadInfo, ): Promise { - const { - forgerAddressBuffer, - forgerAddressBinary, - header: { reward }, - payload, - } = forgerPayloadInfo; + const { forgerAddressBuffer, forgerAddressBinary, payload } = forgerPayloadInfo; const forgerInfo = await getForgerInfo(this._forgerPluginDB, forgerAddressBinary); if (this._forgersList.has(forgerAddressBuffer)) { forgerInfo.totalProducedBlocks -= 1; - forgerInfo.totalReceivedRewards -= BigInt(reward); forgerInfo.totalReceivedFees -= this._getFee(payload, encodedBlock); await setForgerInfo(this._forgerPluginDB, forgerAddressBinary, { ...forgerInfo }); } @@ -293,13 +267,20 @@ export class ForgerPlugin extends BasePlugin { await this._revertVotesReceived(payload); } - private _getForgerReceivedVotes(payload: ReadonlyArray): ForgerReceivedVotes { + private _getForgerReceivedVotes(payload: ReadonlyArray): ForgerReceivedVotes { const forgerReceivedVotes: ForgerReceivedVotes = {}; + const dposVotesSchema = this.apiClient.schemas.commands.find( + c => c.moduleID === MODULE_ID_DPOS && c.commandID === COMMAND_ID_VOTE, + ); + if (!dposVotesSchema) { + throw new Error('DPoS votes command is not registered.'); + } + for (const trx of payload) { - if (trx.moduleID === 5 && trx.assetID === 1) { - const senderAddress = getAddressFromPublicKey(Buffer.from(trx.senderPublicKey, 'hex')); - (trx.asset as Asset).votes.reduce((acc: ForgerReceivedVotes, curr) => { + if (trx.moduleID === MODULE_ID_DPOS && trx.commandID === COMMAND_ID_VOTE) { + const params = codec.decode(dposVotesSchema.schema, trx.params); + params.votes.reduce((acc: ForgerReceivedVotes, curr) => { if ( this._forgersList.has(getAddressBuffer(curr.delegateAddress)) && acc[curr.delegateAddress] @@ -307,7 +288,7 @@ export class ForgerPlugin extends BasePlugin { acc[curr.delegateAddress].amount += BigInt(curr.amount); } else { acc[curr.delegateAddress] = { - address: senderAddress, + address: trx.senderAddress, amount: BigInt(curr.amount), }; } @@ -319,7 +300,7 @@ export class ForgerPlugin extends BasePlugin { return forgerReceivedVotes; } - private async _addVotesReceived(payload: ReadonlyArray): Promise { + private async _addVotesReceived(payload: ReadonlyArray): Promise { const forgerReceivedVotes = this._getForgerReceivedVotes(payload); for (const [delegateAddress, votesReceived] of Object.entries(forgerReceivedVotes)) { @@ -344,7 +325,7 @@ export class ForgerPlugin extends BasePlugin { } } - private async _revertVotesReceived(payload: ReadonlyArray): Promise { + private async _revertVotesReceived(payload: ReadonlyArray): Promise { const forgerReceivedVotes = this._getForgerReceivedVotes(payload); for (const [delegateAddress, votesReceived] of Object.entries(forgerReceivedVotes)) { @@ -367,15 +348,18 @@ export class ForgerPlugin extends BasePlugin { } } - private _getFee(payload: ReadonlyArray, block: string): bigint { - const { payload: payloadBuffer } = this.codec.decodeRawBlock(block); + private _getFee(payload: ReadonlyArray, block: string): bigint { + const { payload: payloadBuffer } = codec.decode<{ payload: Buffer[] }>( + blockSchema, + Buffer.from(block), + ); let fee = BigInt(0); for (let index = 0; index < payload.length; index += 1) { const trx = payload[index]; const baseFee = this._transactionFees.baseFees.find( - bf => bf.moduleID === trx.moduleID && bf.assetID === trx.assetID, + bf => bf.moduleID === trx.moduleID && bf.commandID === trx.commandID, )?.baseFee ?? '0'; const minFeeRequired = BigInt(baseFee) + @@ -391,19 +375,19 @@ export class ForgerPlugin extends BasePlugin { header: { height, timestamp }, forgerAddress, } = this._getForgerHeaderAndPayloadInfo(block); - const previousBlockStr = await this._channel.invoke('app:getBlockByHeight', { + const previousBlockStr = await this._channel.invoke('app_getBlockByHeight', { height: height - 1, }); const { genesisConfig: { blockTime }, - } = await this._channel.invoke('app:getNodeInfo'); - const { header: previousBlock } = this.codec.decodeBlock(previousBlockStr); + } = await this._channel.invoke('app_getNodeInfo'); + const { header: previousBlock } = Block.fromBytes(Buffer.from(previousBlockStr, 'hex')); const missedBlocks = Math.ceil((timestamp - previousBlock.timestamp) / blockTime) - 1; if (missedBlocks > 0) { const forgersInfo = await this._channel.invoke< readonly { address: string; nextForgingTime: number }[] - >('app:getForgers'); + >('app_getForgers'); const forgersRoundLength = forgersInfo.length; const forgerIndex = forgersInfo.findIndex(f => f.address === forgerAddress); diff --git a/framework-plugins/lisk-framework-forger-plugin/src/schemas.ts b/framework-plugins/lisk-framework-forger-plugin/src/schemas.ts index 105e8c25635..0e22c642ca4 100644 --- a/framework-plugins/lisk-framework-forger-plugin/src/schemas.ts +++ b/framework-plugins/lisk-framework-forger-plugin/src/schemas.ts @@ -61,3 +61,33 @@ export const forgerSyncSchema = { }, }, }; + +export const dposVoteParamsSchema = { + $id: 'forger/dpos/vote', + type: 'object', + required: ['votes'], + properties: { + votes: { + type: 'array', + minItems: 1, + maxItems: 20, + items: { + type: 'object', + required: ['delegateAddress', 'amount'], + properties: { + delegateAddress: { + dataType: 'bytes', + fieldNumber: 1, + minLength: 20, + maxLength: 20, + }, + amount: { + dataType: 'sint64', + fieldNumber: 2, + }, + }, + }, + fieldNumber: 1, + }, + }, +}; diff --git a/framework-plugins/lisk-framework-forger-plugin/src/types.ts b/framework-plugins/lisk-framework-forger-plugin/src/types.ts index 6107da39215..710978d2875 100644 --- a/framework-plugins/lisk-framework-forger-plugin/src/types.ts +++ b/framework-plugins/lisk-framework-forger-plugin/src/types.ts @@ -33,7 +33,7 @@ export interface TransactionFees { readonly minFeePerByte: number; readonly baseFees: { readonly moduleID: number; - readonly assetID: number; + readonly commandID: number; readonly baseFee: string; }[]; } diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/blocks.ts b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/blocks.ts index f2e720770fe..a6f66a924b8 100644 --- a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/blocks.ts +++ b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/blocks.ts @@ -11,10 +11,10 @@ * * Removal or modification of this copyright notice is prohibited. */ -import { BaseChannel } from 'lisk-framework'; +import { BasePlugin } from 'lisk-framework'; import { BlockPropagationStats, PeerInfo, SharedState } from '../types'; -interface BlockStats { +export interface BlockStats { readonly blocks: Record; readonly averageReceivedBlocks: number; readonly connectedPeers: number; @@ -31,10 +31,10 @@ const getAverageReceivedBlocks = (blocks: { [key: string]: BlockPropagationStats }; export const getBlockStats = async ( - channel: BaseChannel, + client: BasePlugin['apiClient'], state: SharedState, ): Promise => { - const connectedPeers = await channel.invoke>('app:getConnectedPeers'); + const connectedPeers = await client.invoke>('app_getConnectedPeers'); return { blocks: state.blocks, diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/forks.ts b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/forks.ts index bc3366e1675..8dd8234f396 100644 --- a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/forks.ts +++ b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/forks.ts @@ -19,7 +19,7 @@ interface BlockHeader { timeReceived: number; } -interface ForkStats { +export interface ForkStats { readonly forkEventCount: number; blockHeaders: Record; } diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/network.ts b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/network.ts index 67b6e6054ff..94b141a4839 100644 --- a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/network.ts +++ b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/network.ts @@ -11,10 +11,10 @@ * * Removal or modification of this copyright notice is prohibited. */ -import { BaseChannel } from 'lisk-framework'; +import { BasePlugin } from 'lisk-framework'; import { PeerInfo } from '../types'; -interface NetworkStats { +export interface NetworkStats { [key: string]: unknown; } @@ -36,10 +36,10 @@ const getMajorityHeight = (peers: PeerInfo[]): { height: number; count: number } return majority; }; -export const getNetworkStats = async (channel: BaseChannel): Promise => { - const networkStats = await channel.invoke>('app:getNetworkStats'); - const connectedPeers = await channel.invoke('app:getConnectedPeers'); - const disconnectedPeers = await channel.invoke('app:getDisconnectedPeers'); +export const getNetworkStats = async (channel: BasePlugin['apiClient']): Promise => { + const networkStats = await channel.invoke('app_getNetworkStats'); + const connectedPeers = await channel.invoke('app_getConnectedPeers'); + const disconnectedPeers = await channel.invoke('app_getDisconnectedPeers'); const majorityHeight = getMajorityHeight(connectedPeers); const totalPeers = { connected: connectedPeers.length, diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/prometheus.ts b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/prometheus.ts index 72eb67fef8d..8609a8f1849 100644 --- a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/prometheus.ts +++ b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/prometheus.ts @@ -12,7 +12,7 @@ * Removal or modification of this copyright notice is prohibited. */ import { Request, Response, NextFunction } from 'express'; -import { BaseChannel } from 'lisk-framework'; +import { BasePlugin } from 'lisk-framework'; import { SharedState, PeerInfo } from '../types'; import { getBlockStats } from './blocks'; import { getTransactionStats } from './transactions'; @@ -55,17 +55,17 @@ const prometheusExporter = (data: PrometheusData[]) => { return exportData; }; -export const getData = (channel: BaseChannel, state: SharedState) => async ( +export const getData = (client: BasePlugin['apiClient'], state: SharedState) => async ( _req: Request, res: Response, next: NextFunction, ): Promise => { try { - const connectedPeers: PeerInfo[] = await channel.invoke('app:getConnectedPeers'); - const disconnectedPeers: PeerInfo[] = await channel.invoke('app:getDisconnectedPeers'); - const nodeInfo: NodeInfo = await channel.invoke('app:getNodeInfo'); - const blockStats = await getBlockStats(channel, state); - const transactionStats = await getTransactionStats(channel, state); + const connectedPeers: PeerInfo[] = await client.invoke('app_getConnectedPeers'); + const disconnectedPeers: PeerInfo[] = await client.invoke('app_getDisconnectedPeers'); + const nodeInfo: NodeInfo = await client.invoke('app_getNodeInfo'); + const blockStats = await getBlockStats(client, state); + const transactionStats = await getTransactionStats(client, state); const data: PrometheusData[] = [ { diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/transactions.ts b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/transactions.ts index 49a3c457767..783727c6b96 100644 --- a/framework-plugins/lisk-framework-monitor-plugin/src/controllers/transactions.ts +++ b/framework-plugins/lisk-framework-monitor-plugin/src/controllers/transactions.ts @@ -11,10 +11,10 @@ * * Removal or modification of this copyright notice is prohibited. */ -import { BaseChannel } from 'lisk-framework'; +import { BasePlugin } from 'lisk-framework'; import { PeerInfo, SharedState, TransactionPropagationStats } from '../types'; -interface TransactionStats { +export interface TransactionStats { transactions: Record; connectedPeers: number; averageReceivedTransactions: number; @@ -33,10 +33,10 @@ const getAverage = (transactions: Record): }; export const getTransactionStats = async ( - channel: BaseChannel, + client: BasePlugin['apiClient'], state: SharedState, ): Promise => ({ transactions: state.transactions, - connectedPeers: (await channel.invoke>('app:getConnectedPeers')).length, + connectedPeers: (await client.invoke>('app_getConnectedPeers')).length, averageReceivedTransactions: getAverage(state.transactions), }); diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/endpoint.ts b/framework-plugins/lisk-framework-monitor-plugin/src/endpoint.ts new file mode 100644 index 00000000000..81d99748e1c --- /dev/null +++ b/framework-plugins/lisk-framework-monitor-plugin/src/endpoint.ts @@ -0,0 +1,49 @@ +/* + * Copyright © 2021 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 { BasePlugin, BasePluginEndpoint, PluginEndpointContext } from 'lisk-framework'; +import { SharedState } from './types'; +import * as controllers from './controllers'; + +export class Endpoint extends BasePluginEndpoint { + private _state!: SharedState; + private _client!: BasePlugin['apiClient']; + + public init(state: SharedState, apiClient: BasePlugin['apiClient']) { + this._state = state; + this._client = apiClient; + } + + public async getTransactionStats( + _context: PluginEndpointContext, + ): Promise { + return controllers.transactions.getTransactionStats(this._client, this._state); + } + + public async getBlockStats( + _context: PluginEndpointContext, + ): Promise { + return controllers.blocks.getBlockStats(this._client, this._state); + } + + public async getNetworkStats( + _context: PluginEndpointContext, + ): Promise { + return controllers.network.getNetworkStats(this._client); + } + + // eslint-disable-next-line @typescript-eslint/require-await + public async getForkStats(_context: PluginEndpointContext): Promise { + return controllers.forks.getForkStats(this._state); + } +} diff --git a/framework-plugins/lisk-framework-monitor-plugin/src/monitor_plugin.ts b/framework-plugins/lisk-framework-monitor-plugin/src/monitor_plugin.ts index f5fec4b1109..89e86700a72 100644 --- a/framework-plugins/lisk-framework-monitor-plugin/src/monitor_plugin.ts +++ b/framework-plugins/lisk-framework-monitor-plugin/src/monitor_plugin.ts @@ -12,11 +12,9 @@ * Removal or modification of this copyright notice is prohibited. */ import { Server } from 'http'; -import { RawBlock, RawBlockHeader } from '@liskhq/lisk-chain'; -import { codec } from '@liskhq/lisk-codec'; -import { hash } from '@liskhq/lisk-cryptography'; +import { Block } from '@liskhq/lisk-chain'; import { validator } from '@liskhq/lisk-validator'; -import { ActionsDefinition, BaseChannel, BasePlugin } from 'lisk-framework'; +import { BaseChannel, BasePlugin } from 'lisk-framework'; import * as express from 'express'; import type { Express } from 'express'; import * as cors from 'cors'; @@ -25,6 +23,7 @@ import * as middlewares from './middlewares'; import { MonitorPluginConfig, SharedState } from './types'; import * as controllers from './controllers'; import { transactionAnnouncementSchema, postBlockEventSchema, configSchema } from './schemas'; +import { Endpoint } from './endpoint'; interface BlockData { readonly block: string; @@ -33,30 +32,19 @@ interface BlockData { export class MonitorPlugin extends BasePlugin { public name = 'monitor'; public configSchema = configSchema; + public endpoint = new Endpoint(); private _server!: Server; private _app!: Express; - private _channel!: BaseChannel; private _state!: SharedState; public get nodeModulePath(): string { return __filename; } - public get actions(): ActionsDefinition { - return { - getTransactionStats: async () => - controllers.transactions.getTransactionStats(this._channel, this._state), - getBlockStats: async () => controllers.blocks.getBlockStats(this._channel, this._state), - getNetworkStats: async () => controllers.network.getNetworkStats(this._channel), - getForkStats: () => controllers.forks.getForkStats(this._state), - }; - } - // eslint-disable-next-line @typescript-eslint/require-await - public async load(channel: BaseChannel): Promise { + public async load(_channel: BaseChannel): Promise { this._app = express(); - this._channel = channel; this._state = { forks: { @@ -67,13 +55,11 @@ export class MonitorPlugin extends BasePlugin { blocks: {}, }; - this._channel.once('app:ready', () => { - this._registerMiddlewares(this.config); - this._registerControllers(); - this._registerAfterMiddlewares(this.config); - this._subscribeToEvents(); - this._server = this._app.listen(this.config.port, this.config.host); - }); + this._registerMiddlewares(this.config); + this._registerControllers(); + this._registerAfterMiddlewares(this.config); + this._subscribeToEvents(); + this._server = this._app.listen(this.config.port, this.config.host); } public async unload(): Promise { @@ -107,12 +93,12 @@ export class MonitorPlugin extends BasePlugin { private _registerControllers(): void { this._app.get( '/api/prometheus/metrics', - controllers.prometheusExport.getData(this._channel, this._state), + controllers.prometheusExport.getData(this.apiClient, this._state), ); } private _subscribeToEvents(): void { - this._channel.subscribe('app:network:event', (eventData?: Record) => { + this.apiClient.subscribe('app_networkEvent', (eventData?: Record) => { const { event, data } = eventData as { event: string; data: unknown }; if (event === 'postTransactionsAnnouncement') { @@ -135,7 +121,7 @@ export class MonitorPlugin extends BasePlugin { } }); - this._channel.subscribe('app:chain:fork', (data?: Record) => { + this.apiClient.subscribe('app_chainForked', (data?: Record) => { const { block } = (data as unknown) as BlockData; this._handleFork(block); }); @@ -166,43 +152,34 @@ export class MonitorPlugin extends BasePlugin { private _handleFork(block: string) { this._state.forks.forkEventCount += 1; - const { header } = codec.decode(this.schemas.block, Buffer.from(block, 'hex')); - const blockId = hash(header).toString('hex'); + const { header } = Block.fromBytes(Buffer.from(block, 'hex')); + const blockId = header.id.toString('hex'); if (this._state.forks.blockHeaders[blockId]) { this._state.forks.blockHeaders[blockId].timeReceived = Date.now(); } else { - const decodedHeader = codec.decodeJSON>( - this.schemas.blockHeader, - header, - ); this._state.forks.blockHeaders[blockId] = { - blockHeader: decodedHeader, + blockHeader: header, timeReceived: Date.now(), }; } } private _handlePostBlock(data: BlockData) { - const decodedBlock = codec.decode(this.schemas.block, Buffer.from(data.block, 'hex')); - const decodedBlockHeader = codec.decode( - this.schemas.blockHeader, - decodedBlock.header, - ); - const blockId = hash(decodedBlock.header); + const decodedBlock = Block.fromBytes(Buffer.from(data.block, 'hex')); - if (!this._state.blocks[blockId.toString('hex')]) { - this._state.blocks[blockId.toString('hex')] = { + if (!this._state.blocks[decodedBlock.header.id.toString('hex')]) { + this._state.blocks[decodedBlock.header.id.toString('hex')] = { count: 0, - height: decodedBlockHeader.height, + height: decodedBlock.header.height, }; } - this._state.blocks[blockId.toString('hex')].count += 1; + this._state.blocks[decodedBlock.header.id.toString('hex')].count += 1; // Clean up blocks older than current height minus 300 blocks for (const id of Object.keys(this._state.blocks)) { const blockInfo = this._state.blocks[id]; - if (blockInfo.height < decodedBlockHeader.height - 300) { + if (blockInfo.height < decodedBlock.header.height - 300) { delete this._state.blocks[id]; } } diff --git a/framework-plugins/lisk-framework-report-misbehavior-plugin/src/db.ts b/framework-plugins/lisk-framework-report-misbehavior-plugin/src/db.ts index 51db2ea8af8..a75058251a1 100644 --- a/framework-plugins/lisk-framework-report-misbehavior-plugin/src/db.ts +++ b/framework-plugins/lisk-framework-report-misbehavior-plugin/src/db.ts @@ -18,8 +18,8 @@ import { BlockHeader } from '@liskhq/lisk-chain'; import * as os from 'os'; import { join } from 'path'; import { ensureDir } from 'fs-extra'; -import { RegisteredSchema } from 'lisk-framework'; import { hash } from '@liskhq/lisk-cryptography'; +import { BasePlugin } from 'lisk-framework'; export const blockHeadersSchema = { $id: 'lisk/reportMisbehavior/blockHeaders', @@ -68,29 +68,16 @@ export const getBlockHeaders = async ( } }; -export const decodeBlockHeader = (encodedHeader: Buffer, schema: RegisteredSchema): BlockHeader => { - const id = hash(encodedHeader); - const blockHeader = codec.decode(schema.blockHeader, encodedHeader); - const assetSchema = schema.blockHeadersAssets[blockHeader.version]; - const asset = codec.decode(assetSchema, blockHeader.asset); - return { - ...blockHeader, - asset, - id, - }; -}; - -export const saveBlockHeaders = async ( - db: KVStore, - schemas: RegisteredSchema, - header: Buffer, -): Promise => { - const blockId = hash(header); - const { generatorPublicKey, height } = codec.decode(schemas.blockHeader, header); - const dbKey = Buffer.concat([generatorPublicKey, Buffer.from(':', 'utf8'), formatInt(height)]); +export const saveBlockHeaders = async (db: KVStore, headerBytes: Buffer): Promise => { + const header = BlockHeader.fromBytes(headerBytes); + const dbKey = Buffer.concat([ + header.generatorAddress, + Buffer.from(':', 'utf8'), + formatInt(header.height), + ]); const { blockHeaders } = await getBlockHeaders(db, dbKey); - if (!blockHeaders.find(blockHeader => hash(blockHeader).equals(blockId))) { + if (!blockHeaders.find(blockHeader => hash(blockHeader).equals(header.id))) { await db.put( dbKey, codec.encode(blockHeadersSchema, { @@ -107,37 +94,42 @@ type IteratableStream = NodeJS.ReadableStream & { destroy: (err?: Error) => void export const getContradictingBlockHeader = async ( db: KVStore, blockHeader: BlockHeader, - schemas: RegisteredSchema, -): Promise => - new Promise((resolve, reject) => { + apiClient: BasePlugin['apiClient'], +): Promise => { + const header1 = blockHeader.getBytes().toString('hex'); + const existingHeaders = await new Promise((resolve, reject) => { const stream = db.createReadStream({ - gte: getFirstPrefix(blockHeader.generatorPublicKey), - lte: getLastPrefix(blockHeader.generatorPublicKey), + gte: getFirstPrefix(blockHeader.generatorAddress), + lte: getLastPrefix(blockHeader.generatorAddress), }) as IteratableStream; + const results: string[] = []; stream .on('data', ({ value }: { value: Buffer }) => { const { blockHeaders } = codec.decode(blockHeadersSchema, value); for (const encodedHeader of blockHeaders) { - const decodedBlockHeader = decodeBlockHeader(encodedHeader, schemas); - if (areHeadersContradicting(blockHeader, decodedBlockHeader)) { - stream.destroy(); - resolve(decodedBlockHeader); - } + results.push(encodedHeader.toString('hex')); } }) .on('error', error => { reject(error); }) .on('end', () => { - resolve(undefined); + resolve(results); }); }); + for (const header2 of existingHeaders) { + const contradicting = await apiClient.invoke('bft_areHeadersContradicting', { + header1, + header2, + }); + if (contradicting) { + return BlockHeader.fromBytes(Buffer.from(header2, 'hex')); + } + } + return undefined; +}; -export const clearBlockHeaders = async ( - db: KVStore, - schemas: RegisteredSchema, - currentHeight: number, -): Promise => { +export const clearBlockHeaders = async (db: KVStore, currentHeight: number): Promise => { const keys = await new Promise((resolve, reject) => { const stream = db.createReadStream() as IteratableStream; const res: Buffer[] = []; @@ -145,7 +137,7 @@ export const clearBlockHeaders = async ( .on('data', ({ key, value }: { key: Buffer; value: Buffer }) => { const { blockHeaders } = codec.decode(blockHeadersSchema, value); for (const encodedHeader of blockHeaders) { - const decodedBlockHeader = decodeBlockHeader(encodedHeader, schemas); + const decodedBlockHeader = BlockHeader.fromBytes(encodedHeader); if (decodedBlockHeader.height < currentHeight - 260000) { res.push(key); } diff --git a/framework-plugins/lisk-framework-report-misbehavior-plugin/src/endpoint.ts b/framework-plugins/lisk-framework-report-misbehavior-plugin/src/endpoint.ts new file mode 100644 index 00000000000..7fa66a155bf --- /dev/null +++ b/framework-plugins/lisk-framework-report-misbehavior-plugin/src/endpoint.ts @@ -0,0 +1,65 @@ +/* + * Copyright © 2021 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 { + decryptPassphraseWithPassword, + getAddressAndPublicKeyFromPassphrase, + parseEncryptedPassphrase, +} from '@liskhq/lisk-cryptography'; +import { LiskValidationError, validator } from '@liskhq/lisk-validator'; +import { BasePluginEndpoint, PluginEndpointContext } from 'lisk-framework'; +import { actionParamsSchema } from './schemas'; +import { ReportMisbehaviorPluginConfig, State } from './types'; + +export class Endpoint extends BasePluginEndpoint { + private _state!: State; + private _config!: ReportMisbehaviorPluginConfig; + + public init(state: State, config: ReportMisbehaviorPluginConfig) { + this._state = state; + this._config = config; + } + + // eslint-disable-next-line @typescript-eslint/require-await + public async authorize(context: PluginEndpointContext): Promise<{ result: string }> { + const errors = validator.validate(actionParamsSchema, context.params); + + if (errors.length) { + throw new LiskValidationError([...errors]); + } + + const { enable, password } = context.params; + + try { + const parsedEncryptedPassphrase = parseEncryptedPassphrase(this._config.encryptedPassphrase); + + const passphrase = decryptPassphraseWithPassword( + parsedEncryptedPassphrase, + password as string, + ); + + const { publicKey } = getAddressAndPublicKeyFromPassphrase(passphrase); + + this._state.publicKey = enable ? publicKey : undefined; + this._state.passphrase = enable ? passphrase : undefined; + const changedState = enable ? 'enabled' : 'disabled'; + + return { + result: `Successfully ${changedState} the reporting of misbehavior.`, + }; + } catch (error) { + throw new Error('Password given is not valid.'); + } + } +} diff --git a/framework-plugins/lisk-framework-report-misbehavior-plugin/src/report_misbehavior_plugin.ts b/framework-plugins/lisk-framework-report-misbehavior-plugin/src/report_misbehavior_plugin.ts index 8fdfd809bac..b30f231b197 100644 --- a/framework-plugins/lisk-framework-report-misbehavior-plugin/src/report_misbehavior_plugin.ts +++ b/framework-plugins/lisk-framework-report-misbehavior-plugin/src/report_misbehavior_plugin.ts @@ -11,31 +11,30 @@ * * Removal or modification of this copyright notice is prohibited. */ -import { validator, LiskValidationError } from '@liskhq/lisk-validator'; +import { validator } from '@liskhq/lisk-validator'; import { KVStore } from '@liskhq/lisk-db'; import { codec } from '@liskhq/lisk-codec'; import { BlockHeader, RawBlock, Transaction, TAG_TRANSACTION } from '@liskhq/lisk-chain'; import { - decryptPassphraseWithPassword, - parseEncryptedPassphrase, getAddressAndPublicKeyFromPassphrase, getAddressFromPassphrase, signData, } from '@liskhq/lisk-cryptography'; -import { ActionsDefinition, BasePlugin, BaseChannel } from 'lisk-framework'; +import { BasePlugin, BaseChannel, PluginInitContext } from 'lisk-framework'; import { getDBInstance, saveBlockHeaders, getContradictingBlockHeader, - decodeBlockHeader, clearBlockHeaders, } from './db'; import { ReportMisbehaviorPluginConfig, State } from './types'; -import { postBlockEventSchema, configSchema, actionParamsSchema } from './schemas'; +import { postBlockEventSchema, configSchema } from './schemas'; +import { Endpoint } from './endpoint'; export class ReportMisbehaviorPlugin extends BasePlugin { public name = 'reportMisbehavior'; public configSchema = configSchema; + public endpoint = new Endpoint(); private _pluginDB!: KVStore; private readonly _state: State = { currentHeight: 0 }; @@ -46,41 +45,9 @@ export class ReportMisbehaviorPlugin extends BasePlugin): { result: string } => { - const errors = validator.validate(actionParamsSchema, params as Record); - - if (errors.length) { - throw new LiskValidationError([...errors]); - } - - const { enable, password } = params as Record; - - try { - const parsedEncryptedPassphrase = parseEncryptedPassphrase( - this.config.encryptedPassphrase, - ); - - const passphrase = decryptPassphraseWithPassword( - parsedEncryptedPassphrase, - password as string, - ); - - const { publicKey } = getAddressAndPublicKeyFromPassphrase(passphrase); - - this._state.publicKey = enable ? publicKey : undefined; - this._state.passphrase = enable ? passphrase : undefined; - const changedState = enable ? 'enabled' : 'disabled'; - - return { - result: `Successfully ${changedState} the reporting of misbehavior.`, - }; - } catch (error) { - throw new Error('Password given is not valid.'); - } - }, - }; + public async init(context: PluginInitContext): Promise { + await super.init(context); + this.endpoint.init(this._state, this.config); } // eslint-disable-next-line @typescript-eslint/require-await @@ -93,7 +60,7 @@ export class ReportMisbehaviorPlugin extends BasePlugin { - clearBlockHeaders(this._pluginDB, this.schemas, this._state.currentHeight).catch(error => + clearBlockHeaders(this._pluginDB, this._state.currentHeight).catch(error => this.logger.error(error), ); }, this.config.clearBlockHeadersInterval); @@ -117,15 +84,15 @@ export class ReportMisbehaviorPlugin extends BasePlugin( - this.schemas.block, + this.apiClient.schemas.block, Buffer.from(blockData.block, 'hex'), ); try { - const saved = await saveBlockHeaders(this._pluginDB, this.schemas, header); + const saved = await saveBlockHeaders(this._pluginDB, header); if (!saved) { return; } - const decodedBlockHeader = decodeBlockHeader(header, this.schemas); + const decodedBlockHeader = BlockHeader.fromBytes(header); // Set new currentHeight if (decodedBlockHeader.height > this._state.currentHeight) { @@ -134,7 +101,7 @@ export class ReportMisbehaviorPlugin extends BasePlugin('app:postTransaction', { + }>('app_postTransaction', { transaction: encodedTransaction, }); @@ -161,8 +128,8 @@ export class ReportMisbehaviorPlugin extends BasePlugin { // ModuleID:5 (DPoS), AssetID:3 (PoMAsset) - const pomAssetInfo = this.schemas.transactionsAssets.find( - ({ moduleID, assetID }) => moduleID === 5 && assetID === 3, + const pomAssetInfo = this.apiClient.schemas.commands.find( + ({ moduleID, commandID }) => moduleID === 5 && commandID === 3, ); if (!pomAssetInfo) { @@ -172,36 +139,29 @@ export class ReportMisbehaviorPlugin extends BasePlugin('app:getAccount', { + const authAccount = await this._channel.invoke<{ nonce: string }>('auth_getAuthAccount', { address: getAddressFromPassphrase(passphrase).toString('hex'), }); - const { - sequence: { nonce }, - } = codec.decode<{ sequence: { nonce: bigint } }>( - this.schemas.account, - Buffer.from(encodedAccount, 'hex'), - ); - - const pomTransactionAsset = { + const pomTransactionParams = { header1: decodedBlockHeader, header2: contradictingBlock, }; const { networkIdentifier } = await this._channel.invoke<{ networkIdentifier: string }>( - 'app:getNodeInfo', + 'app_getNodeInfo', ); - const encodedAsset = codec.encode(pomAssetInfo.schema, pomTransactionAsset); + const encodedParams = codec.encode(pomAssetInfo.schema, pomTransactionParams); const tx = new Transaction({ moduleID: pomAssetInfo.moduleID, - assetID: pomAssetInfo.assetID, - nonce, + commandID: pomAssetInfo.commandID, + nonce: BigInt(authAccount.nonce), senderPublicKey: this._state.publicKey ?? getAddressAndPublicKeyFromPassphrase(passphrase).publicKey, fee: BigInt(this.config.fee), // TODO: The static fee should be replaced by fee estimation calculation - asset: encodedAsset, + params: encodedParams, signatures: [], }); diff --git a/framework/src/index.ts b/framework/src/index.ts index cb8eab57409..a87f26a97a3 100644 --- a/framework/src/index.ts +++ b/framework/src/index.ts @@ -22,7 +22,7 @@ export { export { BaseModule, BaseAPI, BaseCommand, BaseEndpoint } from './modules'; export { Application } from './application'; export { systemDirs } from './system_dirs'; -export { BasePlugin } from './plugins/base_plugin'; +export { BasePlugin, PluginInitContext } from './plugins/base_plugin'; export { BasePluginEndpoint } from './plugins/base_plugin_endpoint'; export { IPCChannel } from './controller/channels'; export type { BaseChannel } from './controller/channels'; diff --git a/framework/src/modules/bft/endpoint.ts b/framework/src/modules/bft/endpoint.ts index 975b4ee311c..e655f04640e 100644 --- a/framework/src/modules/bft/endpoint.ts +++ b/framework/src/modules/bft/endpoint.ts @@ -12,6 +12,27 @@ * Removal or modification of this copyright notice is prohibited. */ +import { BlockHeader } from '@liskhq/lisk-chain'; +import { LiskValidationError, validator } from '@liskhq/lisk-validator'; +import { ModuleEndpointContext } from '../../types'; import { BaseEndpoint } from '../base_endpoint'; +import { areHeadersContradictingRequestSchema } from './schemas'; +import { areDistinctHeadersContradicting } from './utils'; -export class BFTEndpoint extends BaseEndpoint {} +export class BFTEndpoint extends BaseEndpoint { + // eslint-disable-next-line @typescript-eslint/require-await + public async areHeadersContradicting(context: ModuleEndpointContext): Promise { + const errors = validator.validate(areHeadersContradictingRequestSchema, context.params); + if (errors.length > 0) { + throw new LiskValidationError(errors); + } + + const bftHeader1 = BlockHeader.fromBytes(Buffer.from(context.params.header1 as string, 'hex')); + const bftHeader2 = BlockHeader.fromBytes(Buffer.from(context.params.header2 as string, 'hex')); + + if (bftHeader1.id.equals(bftHeader2.id)) { + return false; + } + return areDistinctHeadersContradicting(bftHeader1, bftHeader2); + } +} diff --git a/framework/src/modules/bft/schemas.ts b/framework/src/modules/bft/schemas.ts index 4f088a1efb8..9b9798f060b 100644 --- a/framework/src/modules/bft/schemas.ts +++ b/framework/src/modules/bft/schemas.ts @@ -191,3 +191,18 @@ export const validatorsHashInputSchema = { certificateThreshold: { dataType: 'uint64', fieldNumber: 2 }, }, }; + +export interface AreHeadersContradictingRequest { + header1: string; + header2: string; +} + +export const areHeadersContradictingRequestSchema = { + $id: 'modules/bft/areHeadersContradictingRequest', + type: 'object', + required: ['header1', 'header2'], + properties: { + header1: { type: 'string', format: 'hex' }, + header2: { type: 'string', format: 'hex' }, + }, +}; diff --git a/framework/src/plugins/base_plugin.ts b/framework/src/plugins/base_plugin.ts index 0ff462800de..ca2e84a864b 100644 --- a/framework/src/plugins/base_plugin.ts +++ b/framework/src/plugins/base_plugin.ts @@ -22,9 +22,9 @@ import { ApplicationConfigForPlugin, PluginConfig, SchemaWithDefault } from '../ import { ImplementationMissingError } from '../errors'; import { BasePluginEndpoint } from './base_plugin_endpoint'; -interface PluginInitContext { +export interface PluginInitContext> { logger: Logger; - config: PluginConfig; + config: PluginConfig & T; channel: BaseChannel; appConfig: ApplicationConfigForPlugin; } diff --git a/package.json b/package.json index 1cb14787388..bd08af8aedc 100644 --- a/package.json +++ b/package.json @@ -43,14 +43,14 @@ "clean:node_modules": "lerna clean --yes", "clean:full": "npm run clean:node_modules && rm -rf node_modules && yarn && yarn build", "format": "lerna run format", - "lint": "lerna run lint --ignore=@liskhq/lisk-genesis --ignore=lisk-elements --ignore=@liskhq/lisk-framework* --ignore=lisk-commander", + "lint": "lerna run lint --ignore=@liskhq/lisk-genesis --ignore=lisk-elements --ignore=lisk-commander", "lint:fix": "lerna run lint:fix", "test": "lerna run test", "test:elements": "lerna run test --ignore=lisk-commander --ignore=lisk-framework --ignore=lisk-sdk --ignore=@liskhq/lisk-framework*", "test:framework": "lerna run test --scope=lisk-framework", "test:framework-plugins": "lerna run test --scope=@liskhq/lisk-framework-*", "test:commander": "lerna run test --scope=lisk-commander", - "build": "lerna run build --ignore=@liskhq/lisk-genesis --ignore=lisk-elements --ignore=@liskhq/lisk-framework* --ignore=lisk-commander", + "build": "lerna run build --ignore=@liskhq/lisk-genesis --ignore=lisk-commander", "init": "./scripts/init.sh", "prepare": "husky install", "postinstall": "husky install"