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

Add Change Commission command #7757

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions framework/src/modules/dpos_v2/commands/change_commission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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 { validator } from '@liskhq/lisk-validator';
import {
CommandVerifyContext,
VerificationResult,
VerifyStatus,
CommandExecuteContext,
} from '../../../state_machine';
import { BaseCommand } from '../../base_command';
import { CommissionChangeEvent } from '../events/commission_change';
import { changeCommissionCommandParamsSchema } from '../schemas';
import { DelegateStore } from '../stores/delegate';
import { ChangeCommissionParams } from '../types';

export class ChangeCommissionCommand extends BaseCommand {
public schema = changeCommissionCommandParamsSchema;

private _commissionIncreasePeriod!: number;
private _maxCommissionIncreaseRate!: number;

public init(args: { commissionIncreasePeriod: number; maxCommissionIncreaseRate: number }) {
this._commissionIncreasePeriod = args.commissionIncreasePeriod;
this._maxCommissionIncreaseRate = args.maxCommissionIncreaseRate;
}

public async verify(
context: CommandVerifyContext<ChangeCommissionParams>,
): Promise<VerificationResult> {
try {
validator.validate(changeCommissionCommandParamsSchema, context.params);
} catch (err) {
return {
status: VerifyStatus.FAIL,
error: err as Error,
};
}

const delegateStore = this.stores.get(DelegateStore);
const delegateExists = await delegateStore.has(context, context.transaction.senderAddress);

if (!delegateExists) {
return {
status: VerifyStatus.FAIL,
error: new Error('Transaction sender has not registered as a delegate.'),
};
}

const delegate = await delegateStore.get(context, context.transaction.senderAddress);
const oldCommission = delegate.commission;
const hasIncreasedCommissionRecently =
context.header.height - delegate.lastCommissionIncreaseHeight <
this._commissionIncreasePeriod;

if (context.params.newCommission >= oldCommission && hasIncreasedCommissionRecently) {
return {
status: VerifyStatus.FAIL,
error: new Error(
`Can only increase the commission again ${this._commissionIncreasePeriod} blocks after the last commission increase.`,
),
};
}

if (context.params.newCommission - oldCommission > this._maxCommissionIncreaseRate) {
return {
status: VerifyStatus.FAIL,
error: new Error(
`Invalid argument: Commission increase larger than ${this._maxCommissionIncreaseRate}.`,
),
};
}

return {
status: VerifyStatus.OK,
};
}

public async execute(context: CommandExecuteContext<ChangeCommissionParams>): Promise<void> {
const delegateStore = this.stores.get(DelegateStore);

const delegate = await delegateStore.get(context, context.transaction.senderAddress);
const oldCommission = delegate.commission;

delegate.commission = context.params.newCommission;
if (delegate.commission > oldCommission) {
delegate.lastCommissionIncreaseHeight = context.header.height;
}

await delegateStore.set(context, context.transaction.senderAddress, delegate);

this.events.get(CommissionChangeEvent).log(context, {
delegateAddress: context.transaction.senderAddress,
oldCommission,
newCommission: context.params.newCommission,
});
}
}
4 changes: 4 additions & 0 deletions framework/src/modules/dpos_v2/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const LOCAL_ID_LENGTH = 4;
export const TOKEN_ID_LENGTH = CHAIN_ID_LENGTH + LOCAL_ID_LENGTH;
export const MAX_NUMBER_BYTES_Q96 = 24;
export const COMMISSION = 10000;
export const COMMISSION_INCREASE_PERIOD = 260000;
export const MAX_COMMISSION_INCREASE_RATE = 500;

// Key length
export const ED25519_PUBLIC_KEY_LENGTH = 32;
Expand All @@ -62,6 +64,8 @@ export const defaultConfig = {
tokenIDFee: TOKEN_ID_FEE.toString('hex'),
delegateRegistrationFee: DELEGATE_REGISTRATION_FEE.toString(),
maxBFTWeightCap: 500,
commissionIncreasePeriod: COMMISSION_INCREASE_PERIOD,
maxCommissionIncreaseRate: MAX_COMMISSION_INCREASE_RATE,
};

export const enum PoSEventResult {
Expand Down
2 changes: 2 additions & 0 deletions framework/src/modules/dpos_v2/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ export class DPoSEndpoint extends BaseEndpoint {
tokenIDFee: this._moduleConfig.tokenIDFee.toString('hex'),
delegateRegistrationFee: this._moduleConfig.delegateRegistrationFee.toString(),
maxBFTWeightCap: this._moduleConfig.maxBFTWeightCap,
commissionIncreasePeriod: this._moduleConfig.commissionIncreasePeriod,
maxCommissionIncreaseRate: this._moduleConfig.maxCommissionIncreaseRate,
};
}

Expand Down
9 changes: 9 additions & 0 deletions framework/src/modules/dpos_v2/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ReportDelegateMisbehaviorCommand } from './commands/pom';
import { UnlockCommand } from './commands/unlock';
import { UpdateGeneratorKeyCommand } from './commands/update_generator_key';
import { VoteDelegateCommand } from './commands/vote_delegate';
import { ChangeCommissionCommand } from './commands/change_commission';
import {
DELEGATE_LIST_ROUND_OFFSET,
EMPTY_KEY,
Expand Down Expand Up @@ -82,6 +83,7 @@ import { DelegateBannedEvent } from './events/delegate_banned';
import { DelegatePunishedEvent } from './events/delegate_punished';
import { DelegateRegisteredEvent } from './events/delegate_registered';
import { DelegateVotedEvent } from './events/delegate_voted';
import { CommissionChangeEvent } from './events/commission_change';

export class DPoSModule extends BaseModule {
public method = new DPoSMethod(this.stores, this.events);
Expand All @@ -102,6 +104,7 @@ export class DPoSModule extends BaseModule {
this.events,
);
private readonly _voteCommand = new VoteDelegateCommand(this.stores, this.events);
private readonly _changeCommissionCommand = new ChangeCommissionCommand(this.stores, this.events);

// eslint-disable-next-line @typescript-eslint/member-ordering
public commands = [
Expand All @@ -110,6 +113,7 @@ export class DPoSModule extends BaseModule {
this._unlockCommand,
this._updateGeneratorKeyCommand,
this._voteCommand,
this._changeCommissionCommand,
];

private _randomMethod!: RandomMethod;
Expand All @@ -131,6 +135,7 @@ export class DPoSModule extends BaseModule {
this.events.register(DelegatePunishedEvent, new DelegatePunishedEvent(this.name));
this.events.register(DelegateRegisteredEvent, new DelegateRegisteredEvent(this.name));
this.events.register(DelegateVotedEvent, new DelegateVotedEvent(this.name));
this.events.register(CommissionChangeEvent, new CommissionChangeEvent(this.name));
}

public get name() {
Expand Down Expand Up @@ -256,6 +261,10 @@ export class DPoSModule extends BaseModule {
roundLength: this._moduleConfig.roundLength,
});
this._voteCommand.init({ governanceTokenID: this._moduleConfig.governanceTokenID });
this._changeCommissionCommand.init({
commissionIncreasePeriod: this._moduleConfig.commissionIncreasePeriod,
maxCommissionIncreaseRate: this._moduleConfig.maxCommissionIncreaseRate,
});

this.stores.get(EligibleDelegatesStore).init(this._moduleConfig);
}
Expand Down
21 changes: 21 additions & 0 deletions framework/src/modules/dpos_v2/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ export const pomCommandParamsSchema = {
},
};

export const changeCommissionCommandParamsSchema = {
$id: '/dpos/command/changeCommissionCommandParams',
type: 'object',
required: ['newCommission'],
properties: {
newCommission: {
dataType: 'uint32',
fieldNumber: 1,
maximum: MAX_COMMISSION,
},
},
};

export const configSchema = {
$id: '/dpos/config',
type: 'object',
Expand Down Expand Up @@ -180,6 +193,14 @@ export const configSchema = {
minimum: 1,
maximum: 9999,
},
commissionIncreasePeriod: {
type: 'integer',
format: 'uint32',
},
maxCommissionIncreaseRate: {
type: 'integer',
format: 'uint32',
},
},
required: [
'factorSelfVotes',
Expand Down
6 changes: 6 additions & 0 deletions framework/src/modules/dpos_v2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface ModuleConfig {
tokenIDFee: Buffer;
delegateRegistrationFee: bigint;
maxBFTWeightCap: number;
commissionIncreasePeriod: number;
maxCommissionIncreaseRate: number;
}

export type ModuleConfigJSON = JSONObject<ModuleConfig>;
Expand Down Expand Up @@ -214,6 +216,10 @@ export interface PomCommandDependencies {
validatorsMethod: ValidatorsMethod;
}

export interface ChangeCommissionParams {
newCommission: number;
}

export interface ValidatorKeys {
generatorKey: Buffer;
blsKey: Buffer;
Expand Down
Loading