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

Commit

Permalink
🌱 Update unlock command
Browse files Browse the repository at this point in the history
- Add verify()
- Add _isUnlockable()
- Refactor execute()
- Update constants
  • Loading branch information
mosmartin committed Oct 31, 2022
1 parent 15ab6b8 commit 32d8b96
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 17 deletions.
103 changes: 90 additions & 13 deletions framework/src/modules/dpos_v2/commands/unlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,26 @@
* Removal or modification of this copyright notice is prohibited.
*/

import { CommandExecuteContext } from '../../../state_machine/types';
import {
CommandExecuteContext,
CommandVerifyContext,
VerificationResult,
VerifyStatus,
} from '../../../state_machine';
import { BaseCommand } from '../../base_command';
import { EMPTY_KEY, MODULE_NAME_DPOS } from '../constants';
import {
EMPTY_KEY,
LOCKING_PERIOD_SELF_VOTES,
LOCKING_PERIOD_VOTES,
MODULE_NAME_DPOS,
PUNISHMENT_WINDOW_SELF_VOTES,
PUNISHMENT_WINDOW_VOTES,
} from '../constants';
import { DelegateStore } from '../stores/delegate';
import { GenesisDataStore } from '../stores/genesis';
import { VoterStore } from '../stores/voter';
import { TokenMethod, TokenIDDPoS, UnlockCommandDependencies } from '../types';
import { hasWaited, isPunished, isCertificateGenerated } from '../utils';
import { TokenMethod, TokenIDDPoS, UnlockCommandDependencies, UnlockingObject } from '../types';
import { isPunished, isCertificateGenerated } from '../utils';

export class UnlockCommand extends BaseCommand {
private _tokenMethod!: TokenMethod;
Expand All @@ -35,6 +47,23 @@ export class UnlockCommand extends BaseCommand {
this._roundLength = args.roundLength;
}

// TODO: Check verification logic
// eslint-disable-next-line @typescript-eslint/require-await
public async verify(context: CommandVerifyContext): Promise<VerificationResult> {
const { transaction } = context;

if (transaction.params.length !== 0) {
return {
status: VerifyStatus.FAIL,
error: new Error('Unlock transaction params must be empty.'),
};
}

return {
status: VerifyStatus.OK,
};
}

public async execute(context: CommandExecuteContext): Promise<void> {
const {
transaction: { senderAddress },
Expand All @@ -45,7 +74,7 @@ export class UnlockCommand extends BaseCommand {
const delegateSubstore = this.stores.get(DelegateStore);
const voterSubstore = this.stores.get(VoterStore);
const voterData = await voterSubstore.get(context, senderAddress);
const ineligibleUnlocks = [];
// const ineligibleUnlocks = [];
const genesisDataStore = this.stores.get(GenesisDataStore);
const genesisData = await genesisDataStore.get(context, EMPTY_KEY);
const { height: genesisHeight } = genesisData;
Expand All @@ -54,30 +83,78 @@ export class UnlockCommand extends BaseCommand {
const { pomHeights } = await delegateSubstore.get(context, unlockObject.delegateAddress);

if (
hasWaited(unlockObject, senderAddress, height) &&
!isPunished(unlockObject, pomHeights, senderAddress, height) &&
this._isUnlockable(unlockObject, pomHeights, senderAddress, height) &&
isCertificateGenerated({
unlockObject,
genesisHeight,
maxHeightCertified,
roundLength: this._roundLength,
})
) {
// delete unlockObject from voterStore(senderAddress).pendingUnlocks
await voterSubstore.del(context, senderAddress);

await this._tokenMethod.unlock(
getMethodContext(),
senderAddress,
MODULE_NAME_DPOS,
this._tokenIDDPoS,
unlockObject.amount,
);
continue;
}
ineligibleUnlocks.push(unlockObject);

// if (
// hasWaited(unlockObject, senderAddress, height) &&
// !isPunished(unlockObject, pomHeights, senderAddress, height) &&
// isCertificateGenerated({
// unlockObject,
// genesisHeight,
// maxHeightCertified,
// roundLength: this._roundLength,
// })
// ) {
// await this._tokenMethod.unlock(
// getMethodContext(),
// senderAddress,
// MODULE_NAME_DPOS,
// this._tokenIDDPoS,
// unlockObject.amount,
// );
// continue;
// }
// ineligibleUnlocks.push(unlockObject);
}
if (voterData.pendingUnlocks.length === ineligibleUnlocks.length) {
throw new Error('No eligible voter data was found for unlocking');
// if (voterData.pendingUnlocks.length === ineligibleUnlocks.length) {
// throw new Error('No eligible voter data was found for unlocking');
// }
// voterData.pendingUnlocks = ineligibleUnlocks;
// await voterSubstore.set(context, senderAddress, voterData);
}

public _isUnlockable(
unlockObject: UnlockingObject,
pomHeights: ReadonlyArray<number>,
senderAddress: Buffer,
height: number,
): boolean {
const { delegateAddress } = unlockObject;
const lastPomHeight = pomHeights[pomHeights.length - 1];
const lockingPeriod = delegateAddress.equals(senderAddress)
? LOCKING_PERIOD_SELF_VOTES
: LOCKING_PERIOD_VOTES;
const punishmentWindow = delegateAddress.equals(senderAddress)
? PUNISHMENT_WINDOW_SELF_VOTES
: PUNISHMENT_WINDOW_VOTES;

if (!isPunished(unlockObject, pomHeights, delegateAddress, height)) {
if (height - unlockObject.unvoteHeight < lockingPeriod) {
return false;
}
}
voterData.pendingUnlocks = ineligibleUnlocks;
await voterSubstore.set(context, senderAddress, voterData);

return !(
height - lastPomHeight < punishmentWindow &&
lastPomHeight < unlockObject.unvoteHeight + lockingPeriod
);
}
}
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 @@ -36,6 +36,10 @@ export const CHAIN_ID_LENGTH = 4;
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 LOCKING_PERIOD_VOTES = 26_000;
export const LOCKING_PERIOD_SELF_VOTES = 260_000;
export const PUNISHMENT_WINDOW_SELF_VOTES = 780_000;
export const PUNISHMENT_WINDOW_VOTES = 260_000;

export const defaultConfig = {
factorSelfVotes: 10,
Expand Down
6 changes: 2 additions & 4 deletions framework/test/unit/modules/dpos_v2/commands/unlock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import { BlockHeader, Transaction } from '@liskhq/lisk-chain';
import { utils } from '@liskhq/lisk-cryptography';
import * as testing from '../../../../../src/testing';
import { UnlockCommand } from '../../../../../src/modules/dpos_v2/commands/unlock';
import { UnlockCommand, CommandExecuteContext, DPoSModule } from '../../../../../src';
import {
defaultConfig,
EMPTY_KEY,
Expand All @@ -25,13 +25,11 @@ import {
WAIT_TIME_VOTE,
} from '../../../../../src/modules/dpos_v2/constants';
import { TokenMethod, UnlockingObject, VoterData } from '../../../../../src/modules/dpos_v2/types';
import { CommandExecuteContext } from '../../../../../src/state_machine/types';
import { liskToBeddows } from '../../../../utils/assets';
import { PrefixedStateReadWriter } from '../../../../../src/state_machine/prefixed_state_read_writer';
import { InMemoryPrefixedStateDB } from '../../../../../src/testing/in_memory_prefixed_state';
import { InMemoryPrefixedStateDB } from '../../../../../src/testing';
import { DelegateStore } from '../../../../../src/modules/dpos_v2/stores/delegate';
import { VoterStore } from '../../../../../src/modules/dpos_v2/stores/voter';
import { DPoSModule } from '../../../../../src';
import { createStoreGetter } from '../../../../../src/testing/utils';
import { GenesisDataStore } from '../../../../../src/modules/dpos_v2/stores/genesis';

Expand Down

0 comments on commit 32d8b96

Please sign in to comment.