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

Commit

Permalink
Add dynamic reward module (#7737)
Browse files Browse the repository at this point in the history
### What was the problem?

This PR resolves #7717 

### How was it solved?

- Add new dynamic reward module using reward module
- Update genesis block for example app
- Add small refactor to reward module to be usable from dynamic reward
module
- Add new DPoS method which is required by dynamic reward module
  - #7715 should add the implementation for `updateSharedRewards`
### How was it tested?

- Add unit tests for all the cases
  • Loading branch information
shuse2 authored Nov 7, 2022
1 parent d197893 commit 18f7bbe
Show file tree
Hide file tree
Showing 21 changed files with 996 additions and 105 deletions.
Binary file modified examples/dpos-mainchain/config/default/genesis_block.blob
Binary file not shown.
23 changes: 15 additions & 8 deletions framework/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,19 @@ import { ValidatorsMethod, ValidatorsModule } from './modules/validators';
import { TokenModule, TokenMethod } from './modules/token';
import { AuthModule, AuthMethod } from './modules/auth';
import { FeeModule, FeeMethod } from './modules/fee';
import { RewardModule, RewardMethod } from './modules/reward';
import { RandomModule, RandomMethod } from './modules/random';
import { DPoSModule, DPoSMethod } from './modules/dpos_v2';
import { generateGenesisBlock, GenesisBlockGenerateInput } from './genesis_block';
import { StateMachine } from './state_machine';
import { ABIHandler, EVENT_ENGINE_READY } from './abi_handler/abi_handler';
import { ABIServer } from './abi_handler/abi_server';
import { SidechainInteroperabilityModule } from './modules/interoperability/sidechain/module';
import { MainchainInteroperabilityModule } from './modules/interoperability/mainchain/module';
import { SidechainInteroperabilityMethod } from './modules/interoperability/sidechain/method';
import { MainchainInteroperabilityMethod } from './modules/interoperability/mainchain/method';
import {
SidechainInteroperabilityModule,
MainchainInteroperabilityModule,
SidechainInteroperabilityMethod,
MainchainInteroperabilityMethod,
} from './modules/interoperability';
import { DynamicRewardMethod, DynamicRewardModule } from './modules/dynamic_rewards';

const isPidRunning = async (pid: number): Promise<boolean> =>
psList().then(list => list.some(x => x.pid === pid));
Expand Down Expand Up @@ -109,7 +111,7 @@ interface DefaultApplication {
token: TokenMethod;
fee: FeeMethod;
random: RandomMethod;
reward: RewardMethod;
reward: DynamicRewardMethod;
dpos: DPoSMethod;
interoperability: SidechainInteroperabilityMethod | MainchainInteroperabilityMethod;
};
Expand Down Expand Up @@ -163,7 +165,7 @@ export class Application {
const authModule = new AuthModule();
const tokenModule = new TokenModule();
const feeModule = new FeeModule();
const rewardModule = new RewardModule();
const rewardModule = new DynamicRewardModule();
const randomModule = new RandomModule();
const validatorModule = new ValidatorsModule();
const dposModule = new DPoSModule();
Expand All @@ -178,7 +180,12 @@ export class Application {

// resolve dependencies
feeModule.addDependencies(tokenModule.method);
rewardModule.addDependencies(tokenModule.method, randomModule.method);
rewardModule.addDependencies(
tokenModule.method,
randomModule.method,
validatorModule.method,
dposModule.method,
);
dposModule.addDependencies(randomModule.method, validatorModule.method, tokenModule.method);
tokenModule.addDependencies(interoperabilityModule.method);

Expand Down
41 changes: 37 additions & 4 deletions framework/src/modules/dpos_v2/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@
* Removal or modification of this copyright notice is prohibited.
*/

import { ImmutableMethodContext } from '../../state_machine';
import { ImmutableMethodContext, MethodContext } from '../../state_machine';
import { BaseMethod } from '../base_method';
import { MAX_LENGTH_NAME } from './constants';
import { EMPTY_KEY, MAX_LENGTH_NAME } from './constants';
import { GenesisDataStore } from './stores/genesis';
import { VoterStore, VoterData } from './stores/voter';
import { ModuleConfig } from './types';
import { DelegateAccount, DelegateStore } from './stores/delegate';
import { NameStore } from './stores/name';
import { VoterStore } from './stores/voter';
import { VoterData } from './types';
import { isUsername } from './utils';

export class DPoSMethod extends BaseMethod {
private _config!: ModuleConfig;

public init(config: ModuleConfig) {
this._config = config;
}
public async isNameAvailable(
methodContext: ImmutableMethodContext,
name: string,
Expand Down Expand Up @@ -58,4 +64,31 @@ export class DPoSMethod extends BaseMethod {

return delegate;
}

public getRoundLength(_methodContext: ImmutableMethodContext): number {
return this._config.roundLength;
}

public getNumberOfActiveDelegates(_methodContext: ImmutableMethodContext): number {
return this._config.numberActiveDelegates;
}

public async updateSharedRewards(
_methodContext: MethodContext,
_generatorAddress: Buffer,
_tokenID: Buffer,
_reward: bigint,
): Promise<void> {
// TODO: Implement #7715
}

public async isEndOfRound(
methodContext: ImmutableMethodContext,
height: number,
): Promise<boolean> {
const { height: genesisHeight } = await this.stores
.get(GenesisDataStore)
.get(methodContext, EMPTY_KEY);
return (height - genesisHeight) % this._config.roundLength === 0;
}
}
14 changes: 5 additions & 9 deletions framework/src/modules/dpos_v2/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ export class DPoSModule extends BaseModule {

this._moduleConfig = getModuleConfig(config);

this.method.init(this._moduleConfig);
this.endpoint.init(this.name, this._moduleConfig, this._tokenMethod);

this._reportDelegateMisbehaviorCommand.init({
Expand Down Expand Up @@ -498,7 +499,10 @@ export class DPoSModule extends BaseModule {

public async afterTransactionsExecute(context: BlockAfterExecuteContext): Promise<void> {
const { header } = context;
const isLastBlockOfRound = this._isLastBlockOfTheRound(header.height);
const isLastBlockOfRound = await this.method.isEndOfRound(
context.getMethodContext(),
header.height,
);
const previousTimestampStore = this.stores.get(PreviousTimestampStore);
const previousTimestampData = await previousTimestampStore.get(context, EMPTY_KEY);
const { timestamp: previousTimestamp } = previousTimestampData;
Expand Down Expand Up @@ -675,14 +679,6 @@ export class DPoSModule extends BaseModule {
await delegateStore.set(context, header.generatorAddress, generator);
}

private _isLastBlockOfTheRound(height: number): boolean {
const rounds = new Rounds({ blocksPerRound: this._moduleConfig.roundLength });
const currentRound = rounds.calcRound(height);
const nextRound = rounds.calcRound(height + 1);

return currentRound < nextRound;
}

private async _didBootstrapRoundsEnd(context: BlockAfterExecuteContext) {
const { header } = context;
const rounds = new Rounds({ blocksPerRound: this._moduleConfig.roundLength });
Expand Down
24 changes: 24 additions & 0 deletions framework/src/modules/dynamic_rewards/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 { defaultConfig as rewardDefaultConfig } from '../reward/constants';

export const EMPTY_BYTES = Buffer.alloc(0);

export const defaultConfig = {
...rewardDefaultConfig,
factorMinimumRewardActiveDelegates: 1000,
};

export const DECIMAL_PERCENT_FACTOR = BigInt(10000);
16 changes: 16 additions & 0 deletions framework/src/modules/dynamic_rewards/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 { RewardEndpoint } from '../reward/endpoint';

export class DynamicRewardEndpoint extends RewardEndpoint {}
16 changes: 16 additions & 0 deletions framework/src/modules/dynamic_rewards/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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.
*/

export { DynamicRewardModule } from './module';
export { DynamicRewardMethod } from './method';
34 changes: 34 additions & 0 deletions framework/src/modules/dynamic_rewards/method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { ImmutableMethodContext } from '../../state_machine';
import { BaseMethod } from '../base_method';
import { calculateDefaultReward } from '../reward/calculate_reward';
import { ModuleConfig } from './types';

export interface MethodInitArgs {
config: ModuleConfig;
}

export class DynamicRewardMethod extends BaseMethod {
private _config!: ModuleConfig;

public init(args: MethodInitArgs) {
this._config = args.config;
}

public getDefaultRewardAtHeight(_context: ImmutableMethodContext, height: number): bigint {
return calculateDefaultReward(this._config, height);
}
}
Loading

0 comments on commit 18f7bbe

Please sign in to comment.