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

PoA Improvements #2 #8991

Merged
merged 6 commits into from
Sep 20, 2023
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
41 changes: 18 additions & 23 deletions framework/src/modules/poa/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
SUBSTORE_PREFIX_NAME_INDEX,
SUBSTORE_PREFIX_SNAPSHOT_INDEX,
} from './constants';
import { shuffleValidatorList } from './utils';
import { shuffleValidatorList } from '../utils';
import { NextValidatorsSetter, MethodContext } from '../../state_machine/types';
import {
configSchema,
Expand All @@ -52,6 +52,7 @@ import {
RandomMethod,
ModuleConfigJSON,
ModuleConfig,
ActiveValidator,
} from './types';
import { RegisterAuthorityCommand } from './commands/register_authority';
import { UpdateAuthorityCommand } from './commands/update_authority';
Expand Down Expand Up @@ -179,7 +180,10 @@ export class PoAModule extends BaseModule {
previousLengthValidators,
);

const nextValidators = shuffleValidatorList(randomSeed, snapshot1.validators);
const nextValidators = shuffleValidatorList<ActiveValidator>(
randomSeed,
snapshot1.validators,
);

await this._validatorsMethod.setValidatorsParams(
context as MethodContext,
Expand Down Expand Up @@ -220,23 +224,18 @@ export class PoAModule extends BaseModule {
throw new Error('`address` property of all entries in validators must be distinct.');
}

const sortedValidatorsByAddress = [...validatorAddresses].sort((a, b) => a.compare(b));
for (let i = 0; i < validators.length; i += 1) {
// Check that entries in the validators array are ordered lexicographically according to address.
if (!validatorAddresses[i].equals(sortedValidatorsByAddress[i])) {
throw new Error('`validators` must be ordered lexicographically by address.');
}
if (!objects.isBufferArrayOrdered(validatorAddresses)) {
throw new Error('`validators` must be ordered lexicographically by address.');
}

if (!POA_VALIDATOR_NAME_REGEX.test(validators[i].name)) {
for (const poaValidator of validators) {
if (!POA_VALIDATOR_NAME_REGEX.test(poaValidator.name)) {
throw new Error('`name` property is invalid. Must contain only characters a-z0-9!@$&_.');
}
}

const { activeValidators, threshold } = snapshotSubstore;
const activeValidatorAddresses = activeValidators.map(v => v.address);
const sortedActiveValidatorsByAddress = [...activeValidatorAddresses].sort((a, b) =>
a.compare(b),
);
const validatorAddressesString = validatorAddresses.map(a => a.toString('hex'));
let totalWeight = BigInt(0);

Expand All @@ -245,25 +244,21 @@ export class PoAModule extends BaseModule {
throw new Error('`address` properties in `activeValidators` must be distinct.');
}

for (let i = 0; i < activeValidators.length; i += 1) {
// Check that entries in the snapshotSubstore.activeValidators array are ordered lexicographically according to address.
if (!activeValidators[i].address.equals(sortedActiveValidatorsByAddress[i])) {
throw new Error(
'`activeValidators` must be ordered lexicographically by address property.',
);
}

if (!objects.isBufferArrayOrdered(activeValidatorAddresses)) {
throw new Error('`activeValidators` must be ordered lexicographically by address property.');
}
for (const activeValidator of activeValidators) {
// Check that for every element activeValidator in the snapshotSubstore.activeValidators array, there is an entry validator in the validators array with validator.address == activeValidator.address.
if (!validatorAddressesString.includes(activeValidators[i].address.toString('hex'))) {
if (!validatorAddressesString.includes(activeValidator.address.toString('hex'))) {
throw new Error('`activeValidator` address is missing from validators array.');
}

// Check that the weight property of every entry in the snapshotSubstore.activeValidators array is a positive integer.
if (activeValidators[i].weight <= BigInt(0)) {
if (activeValidator.weight <= BigInt(0)) {
throw new Error('`activeValidators` weight must be positive integer.');
}

totalWeight += activeValidators[i].weight;
totalWeight += activeValidator.weight;
}

if (totalWeight > MAX_UINT64) {
Expand Down
8 changes: 1 addition & 7 deletions framework/src/modules/poa/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export type ModuleConfigJSON = JSONObject<ModuleConfig>;
export interface RegisterAuthorityParams {
name: string;
blsKey: Buffer;
generatorKey: Buffer;
proofOfPossession: Buffer;
generatorKey: Buffer;
}

export interface UpdateAuthorityParams {
Expand All @@ -42,12 +42,6 @@ export interface UpdateAuthorityParams {
aggregationBits: Buffer;
}

export interface ValidatorWeightWithRoundHash {
readonly address: Buffer;
weight: bigint;
roundHash: Buffer;
}

export interface ValidatorsMethod {
setValidatorGeneratorKey(
methodContext: MethodContext,
Expand Down
42 changes: 0 additions & 42 deletions framework/src/modules/poa/utils.ts
Original file line number Diff line number Diff line change
@@ -1,42 +0,0 @@
/*
* 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 { utils } from '@liskhq/lisk-cryptography';
import { ValidatorWeightWithRoundHash, ActiveValidator } from './types';

// Same as pos/utils/shuffleValidatorList
export const shuffleValidatorList = (
roundSeed: Buffer,
validators: ActiveValidator[],
): ValidatorWeightWithRoundHash[] => {
const validatorsWithRoundHash: ValidatorWeightWithRoundHash[] = [];
for (const validator of validators) {
const seedSource = Buffer.concat([roundSeed, validator.address]);
validatorsWithRoundHash.push({
...validator,
roundHash: utils.hash(seedSource),
});
}

validatorsWithRoundHash.sort((validator1, validator2) => {
const diff = validator1.roundHash.compare(validator2.roundHash);
if (diff !== 0) {
return diff;
}

return validator1.address.compare(validator2.address);
});

return validatorsWithRoundHash;
};
6 changes: 3 additions & 3 deletions framework/src/modules/pos/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,11 @@ import {
equalUnlocking,
isUsername,
selectStandbyValidators,
shuffleValidatorList,
sortUnlocking,
getModuleConfig,
getValidatorWeight,
ValidatorWeight,
isSharingCoefficientSorted,
ValidatorWeight,
} from './utils';
import { ValidatorStore } from './stores/validator';
import { GenesisDataStore } from './stores/genesis';
Expand All @@ -93,6 +92,7 @@ import { CommissionChangeEvent } from './events/commission_change';
import { ClaimRewardsCommand } from './commands/claim_rewards';
import { getMainchainID } from '../interoperability/utils';
import { RewardsAssignedEvent } from './events/rewards_assigned';
import { shuffleValidatorList } from '../utils';

export class PoSModule extends BaseModule {
public method = new PoSMethod(this.stores, this.events);
Expand Down Expand Up @@ -690,7 +690,7 @@ export class PoSModule extends BaseModule {
}

// Update the validators
const shuffledValidators = shuffleValidatorList(randomSeed1, validators);
const shuffledValidators = shuffleValidatorList<ValidatorWeight>(randomSeed1, validators);
let aggregateBFTWeight = BigInt(0);
const bftValidators: { address: Buffer; bftWeight: bigint }[] = [];
for (const v of shuffledValidators) {
Expand Down
27 changes: 1 addition & 26 deletions framework/src/modules/pos/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* Removal or modification of this copyright notice is prohibited.
*/

import { utils, ed } from '@liskhq/lisk-cryptography';
import { ed } from '@liskhq/lisk-cryptography';
import { math } from '@liskhq/lisk-utils';
import {
ModuleConfig,
Expand Down Expand Up @@ -99,31 +99,6 @@ export const pickStandByValidator = (
return -1;
};

export const shuffleValidatorList = (
previousRoundSeed1: Buffer,
addresses: ValidatorWeight[],
): ValidatorWeight[] => {
const validatorList = [...addresses].map(validator => ({
...validator,
})) as { address: Buffer; roundHash: Buffer; weight: bigint }[];

for (const validator of validatorList) {
const seedSource = Buffer.concat([previousRoundSeed1, validator.address]);
validator.roundHash = utils.hash(seedSource);
}

validatorList.sort((validator1, validator2) => {
const diff = validator1.roundHash.compare(validator2.roundHash);
if (diff !== 0) {
return diff;
}

return validator1.address.compare(validator2.address);
});

return validatorList;
};

export const selectStandbyValidators = (
validatorWeights: ValidatorWeight[],
randomSeed1: Buffer,
Expand Down
15 changes: 15 additions & 0 deletions framework/src/modules/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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.
*/

export { shuffleValidatorList } from './shuffleValidatorList';
46 changes: 46 additions & 0 deletions framework/src/modules/utils/shuffleValidatorList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 { utils } from '@liskhq/lisk-cryptography';

export const shuffleValidatorList = <
T extends {
readonly address: Buffer;
weight: bigint;
},
>(
roundSeed: Buffer,
addresses: T[],
): (T & { roundHash: Buffer })[] => {
const validatorList = [...addresses].map(validator => ({
...validator,
roundHash: Buffer.from([]),
})) as (T & { roundHash: Buffer })[];

for (const validator of validatorList) {
const seedSource = Buffer.concat([roundSeed, validator.address]);
validator.roundHash = utils.hash(seedSource);
}

validatorList.sort((validator1, validator2) => {
const diff = validator1.roundHash.compare(validator2.roundHash);
if (diff !== 0) {
return diff;
}

return validator1.address.compare(validator2.address);
});

return validatorList;
};
2 changes: 1 addition & 1 deletion framework/src/modules/validators/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ export class ValidatorsMethod extends BaseMethod {
throw new Error(`BLS public key must be ${BLS_PUBLIC_KEY_LENGTH} bytes long.`);
}
if (args.proofOfPossession && args.proofOfPossession.length !== BLS_POP_LENGTH) {
throw new Error(`Proof of possesion must be ${BLS_POP_LENGTH} bytes long.`);
throw new Error(`Proof of Possession must be ${BLS_POP_LENGTH} bytes long.`);
}
if (args.generatorKey && args.generatorKey.length !== ED25519_PUBLIC_KEY_LENGTH) {
throw new Error(`Generator key must be ${ED25519_PUBLIC_KEY_LENGTH} bytes long.`);
Expand Down
5 changes: 3 additions & 2 deletions framework/test/unit/modules/poa/module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
LENGTH_GENERATOR_KEY,
} from '../../../../src/modules/poa/constants';
import {
ActiveValidator,
FeeMethod,
ModuleConfigJSON,
RandomMethod,
Expand All @@ -55,7 +56,7 @@ import {
SnapshotObject,
ChainProperties,
} from '../../../../src/modules/poa/stores';
import { shuffleValidatorList } from '../../../../src/modules/poa/utils';
import { shuffleValidatorList } from '../../../../src/modules/utils';

describe('PoA module', () => {
let poaModule: PoAModule;
Expand Down Expand Up @@ -306,7 +307,7 @@ describe('PoA module', () => {
for (const validator of snapshot1.validators) {
validators.push(validator);
}
const nextValidators = shuffleValidatorList(randomSeed, validators);
const nextValidators = shuffleValidatorList<ActiveValidator>(randomSeed, validators);
await poaModule.afterTransactionsExecute(context);
expect(poaModule.stores.get(SnapshotStore).set).toHaveBeenCalledWith(
context,
Expand Down
50 changes: 0 additions & 50 deletions framework/test/unit/modules/poa/utils.spec.ts

This file was deleted.

Loading