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

Commit

Permalink
Add keys info to generator:status and create singleCommits when enabl…
Browse files Browse the repository at this point in the history
…ing generator (#9069)

* 🌱 Add keys information to status

* 🐛 Fix single commits when enabling generator

* 🐛 remove unused metrics
  • Loading branch information
shuse2 committed Oct 6, 2023
1 parent c589442 commit a420fcd
Show file tree
Hide file tree
Showing 7 changed files with 543 additions and 299 deletions.
9 changes: 9 additions & 0 deletions framework/src/engine/generator/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { RequestContext } from '../rpc/rpc_server';
import { ABI } from '../../abi';
import { JSONObject } from '../../types';
import { NotFoundError } from './errors';
import { SingleCommitHandler } from './single_commit_handler';

interface EndpointArgs {
keypair: dataStructures.BufferMap<Keypair>;
Expand All @@ -64,6 +65,7 @@ interface EndpointArgs {

interface EndpointInit {
generatorDB: Database;
singleCommitHandler: SingleCommitHandler;
genesisHeight: number;
}

Expand All @@ -77,6 +79,7 @@ export class Endpoint {

private _generatorDB!: Database;
private _genesisHeight!: number;
private _singleCommitHandler!: SingleCommitHandler;

public constructor(args: EndpointArgs) {
this._keypairs = args.keypair;
Expand All @@ -89,15 +92,19 @@ export class Endpoint {
public init(args: EndpointInit) {
this._generatorDB = args.generatorDB;
this._genesisHeight = args.genesisHeight;
this._singleCommitHandler = args.singleCommitHandler;
}

public async getStatus(_ctx: RequestContext): Promise<GetStatusResponse> {
const generatorStore = new GeneratorStore(this._generatorDB);
const list = await getGeneratedInfo(generatorStore);
const status = [];
for (const info of list) {
const keys = this._keypairs.get(info.address);
status.push({
...info,
generatorKey: keys?.publicKey.toString('hex') ?? '',
blsKey: keys?.blsPublicKey.toString('hex') ?? '',
address: cryptoAddress.getLisk32AddressFromAddress(info.address),
enabled: this._keypairs.has(info.address),
});
Expand Down Expand Up @@ -196,6 +203,8 @@ export class Endpoint {
);
}

await this._singleCommitHandler.initSingleCommits(address);

ctx.logger.info(`Block generation enabled on address: ${req.address}`);

return {
Expand Down
105 changes: 15 additions & 90 deletions framework/src/engine/generator/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import { BFTModule } from '../bft';
import { isEmptyConsensusUpdate } from '../consensus';
import { getPathFromDataPath } from '../../utils/path';
import { defaultMetrics } from '../metrics/metrics';
import { SingleCommitHandler } from './single_commit_handler';

interface GeneratorArgs {
config: EngineConfig;
Expand Down Expand Up @@ -111,14 +112,14 @@ export class Generator {
private readonly _forgingStrategy: HighFeeGenerationStrategy;
private readonly _blockTime: number;
private readonly _metrics = {
signedCommits: defaultMetrics.counter('generator_signedCommits'),
blockGeneration: defaultMetrics.counter('generator_blockGeneration'),
};

private _logger!: Logger;
private _generatorDB!: Database;
private _blockchainDB!: Database;
private _genesisHeight!: number;
private _singleCommitHandler!: SingleCommitHandler;

public constructor(args: GeneratorArgs) {
this._abi = args.abi;
Expand Down Expand Up @@ -174,12 +175,22 @@ export class Generator {
this._blockchainDB = args.blockchainDB;
this._genesisHeight = args.genesisHeight;

this._singleCommitHandler = new SingleCommitHandler(
this._logger,
this._chain,
this._consensus,
this._bft,
this._keypairs,
this._blockchainDB,
);

this._broadcaster.init({
logger: this._logger,
});
this._endpoint.init({
generatorDB: this._generatorDB,
genesisHeight: this._genesisHeight,
singleCommitHandler: this._singleCommitHandler,
});
this._networkEndpoint.init({
logger: this._logger,
Expand Down Expand Up @@ -209,14 +220,7 @@ export class Generator {
this.events.emit(GENERATOR_EVENT_NEW_TRANSACTION, e);
});

const stateStore = new StateStore(this._blockchainDB);

// On node start, it re generates certificate from maxRemovalHeight to maxHeightPrecommitted.
// in the _handleFinalizedHeightChanged, it loops between maxRemovalHeight + 1 and maxHeightPrecommitted.
// @see https://github.com/LiskHQ/lips/blob/main/proposals/lip-0061.md#initial-single-commit-creation
const maxRemovalHeight = await this._consensus.getMaxRemovalHeight();
const { maxHeightPrecommitted } = await this._bft.method.getBFTHeights(stateStore);
await Promise.all(this._handleFinalizedHeightChanged(maxRemovalHeight, maxHeightPrecommitted));
await this._singleCommitHandler.initAllSingleCommits();
}

public get endpoint(): Endpoint {
Expand Down Expand Up @@ -258,11 +262,8 @@ export class Generator {
this._consensus.events.on(
CONSENSUS_EVENT_FINALIZED_HEIGHT_CHANGED,
({ from, to }: { from: number; to: number }) => {
this._consensus
.getMaxRemovalHeight()
.then(async maxRemovalHeight =>
Promise.all(this._handleFinalizedHeightChanged(Math.max(maxRemovalHeight, from), to)),
)
this._singleCommitHandler
.handleFinalizedHeightChanged(from, to)
.catch((err: Error) => this._logger.error({ err }, 'Fail to certify single commit'));
},
);
Expand Down Expand Up @@ -648,82 +649,6 @@ export class Generator {
return generatedBlock;
}

private _handleFinalizedHeightChanged(from: number, to: number): Promise<void>[] {
if (from >= to) {
return [];
}
const promises = [];
const stateStore = new StateStore(this._blockchainDB);
for (const [address, pairs] of this._keypairs.entries()) {
for (let height = from + 1; height < to; height += 1) {
promises.push(
this._certifySingleCommitForChangedHeight(
stateStore,
height,
address,
pairs.blsPublicKey,
pairs.blsSecretKey,
),
);
}
promises.push(
this._certifySingleCommit(stateStore, to, address, pairs.blsPublicKey, pairs.blsSecretKey),
);
}
return promises;
}

private async _certifySingleCommitForChangedHeight(
stateStore: StateStore,
height: number,
generatorAddress: Buffer,
blsPK: Buffer,
blsSK: Buffer,
): Promise<void> {
const paramExist = await this._bft.method.existBFTParameters(stateStore, height + 1);
if (!paramExist) {
return;
}
await this._certifySingleCommit(stateStore, height, generatorAddress, blsPK, blsSK);
}

private async _certifySingleCommit(
stateStore: StateStore,
height: number,
generatorAddress: Buffer,
blsPK: Buffer,
blsSK: Buffer,
): Promise<void> {
const params = await this._bft.method.getBFTParametersActiveValidators(stateStore, height);
const registeredValidator = params.validators.find(v => v.address.equals(generatorAddress));
if (!registeredValidator) {
return;
}
if (!registeredValidator.blsKey.equals(blsPK)) {
this._logger.warn(
{ address: addressUtil.getLisk32AddressFromAddress(generatorAddress) },
'Validator does not have registered BLS key',
);
return;
}

const blockHeader = await this._chain.dataAccess.getBlockHeaderByHeight(height);
const validatorInfo = {
address: generatorAddress,
blsPublicKey: blsPK,
blsSecretKey: blsSK,
};
this._consensus.certifySingleCommit(blockHeader, validatorInfo);
this._logger.debug(
{
height,
generator: addressUtil.getLisk32AddressFromAddress(generatorAddress),
},
'Certified single commit',
);
this._metrics.signedCommits.inc(1);
}

private async _executeTransactions(
contextID: Buffer,
header: BlockHeader,
Expand Down
2 changes: 2 additions & 0 deletions framework/src/engine/generator/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export interface GetStatusResponse {
height: number;
maxHeightPrevoted: number;
maxHeightGenerated: number;
blsKey: string;
generatorKey: string;
enabled: boolean;
}[];
}
Expand Down
166 changes: 166 additions & 0 deletions framework/src/engine/generator/single_commit_handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* 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 { Chain, StateStore } from '@liskhq/lisk-chain';
import { dataStructures } from '@liskhq/lisk-utils';
import { address as addressUtil } from '@liskhq/lisk-cryptography';
import { Database } from '@liskhq/lisk-db';
import { BFTModule } from '../bft';
import { Consensus, Keypair } from './types';
import { Logger } from '../../logger';
import { defaultMetrics } from '../metrics/metrics';

export class SingleCommitHandler {
private readonly _logger: Logger;
private readonly _bft: BFTModule;
private readonly _chain: Chain;
private readonly _consensus: Consensus;
private readonly _keypairs: dataStructures.BufferMap<Keypair>;
private readonly _blockchainDB: Database;

private readonly _metrics = {
signedCommits: defaultMetrics.counter('generator_signedCommits'),
};

public constructor(
logger: Logger,
chain: Chain,
consensus: Consensus,
bft: BFTModule,
keypairs: dataStructures.BufferMap<Keypair>,
blockchainDB: Database,
) {
this._logger = logger;
this._chain = chain;
this._consensus = consensus;
this._bft = bft;
this._keypairs = keypairs;
this._blockchainDB = blockchainDB;
}

// On node start, it re generates certificate from maxRemovalHeight to maxHeightPrecommitted.
// in the _handleFinalizedHeightChanged, it loops between maxRemovalHeight + 1 and maxHeightPrecommitted.
// @see https://github.com/LiskHQ/lips/blob/main/proposals/lip-0061.md#initial-single-commit-creation
public async initAllSingleCommits() {
for (const [address] of this._keypairs.entries()) {
await this.initSingleCommits(address);
}
}

public async initSingleCommits(address: Buffer) {
const maxRemovalHeight = await this._consensus.getMaxRemovalHeight();
const stateStore = new StateStore(this._blockchainDB);
const { maxHeightPrecommitted } = await this._bft.method.getBFTHeights(stateStore);
await Promise.all(
this._handleFinalizedHeightChanged(address, maxRemovalHeight, maxHeightPrecommitted),
);
}

public async handleFinalizedHeightChanged(from: number, to: number): Promise<void> {
const maxRemovalHeight = await this._consensus.getMaxRemovalHeight();
const cappedFrom = Math.max(maxRemovalHeight, from);
if (cappedFrom >= to) {
return;
}
for (const [address] of this._keypairs.entries()) {
await Promise.all(this._handleFinalizedHeightChanged(address, cappedFrom, to));
}
}

private _handleFinalizedHeightChanged(
address: Buffer,
from: number,
to: number,
): Promise<void>[] {
if (from >= to) {
return [];
}
const promises = [];
const stateStore = new StateStore(this._blockchainDB);
const pairs = this._keypairs.get(address);
if (!pairs) {
this._logger.warn(
{ address: addressUtil.getLisk32AddressFromAddress(address) },
'Validator does not have registered BLS key on this node',
);
return [];
}
for (let height = from + 1; height < to; height += 1) {
promises.push(
this._certifySingleCommitForChangedHeight(
stateStore,
height,
address,
pairs.blsPublicKey,
pairs.blsSecretKey,
),
);
}
promises.push(
this._certifySingleCommit(stateStore, to, address, pairs.blsPublicKey, pairs.blsSecretKey),
);
return promises;
}

private async _certifySingleCommitForChangedHeight(
stateStore: StateStore,
height: number,
generatorAddress: Buffer,
blsPK: Buffer,
blsSK: Buffer,
): Promise<void> {
const paramExist = await this._bft.method.existBFTParameters(stateStore, height + 1);
if (!paramExist) {
return;
}
await this._certifySingleCommit(stateStore, height, generatorAddress, blsPK, blsSK);
}

private async _certifySingleCommit(
stateStore: StateStore,
height: number,
generatorAddress: Buffer,
blsPK: Buffer,
blsSK: Buffer,
): Promise<void> {
const params = await this._bft.method.getBFTParametersActiveValidators(stateStore, height);
const registeredValidator = params.validators.find(v => v.address.equals(generatorAddress));
if (!registeredValidator) {
return;
}
if (!registeredValidator.blsKey.equals(blsPK)) {
this._logger.warn(
{ address: addressUtil.getLisk32AddressFromAddress(generatorAddress) },
'Validator does not have registered BLS key',
);
return;
}

const blockHeader = await this._chain.dataAccess.getBlockHeaderByHeight(height);
const validatorInfo = {
address: generatorAddress,
blsPublicKey: blsPK,
blsSecretKey: blsSK,
};
this._consensus.certifySingleCommit(blockHeader, validatorInfo);
this._logger.debug(
{
height,
generator: addressUtil.getLisk32AddressFromAddress(generatorAddress),
},
'Certified single commit',
);
this._metrics.signedCommits.inc(1);
}
}
Loading

0 comments on commit a420fcd

Please sign in to comment.