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

Validate lengths of arguments used in Validator module methods and endpoints #8920

Merged
merged 3 commits into from
Sep 1, 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
55 changes: 31 additions & 24 deletions framework/src/modules/validators/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BaseMethod } from '../base_method';
import { MethodContext, ImmutableMethodContext } from '../../state_machine';
import {
ADDRESS_LENGTH,
BLS_POP_LENGTH,
BLS_PUBLIC_KEY_LENGTH,
ED25519_PUBLIC_KEY_LENGTH,
EMPTY_KEY,
Expand All @@ -31,6 +32,13 @@ import { BlsKeyRegistrationEvent } from './events/bls_key_registration';
import { ValidatorsParams, ValidatorsParamsStore } from './stores/validators_params';
import { NextValidatorsSetter, Validator } from '../../state_machine/types';

export type ValidatorArgs = {
validatorAddress?: Buffer;
blsKey?: Buffer;
proofOfPossession?: Buffer;
generatorKey?: Buffer;
};

export class ValidatorsMethod extends BaseMethod {
private _blockTime!: number;

Expand All @@ -45,9 +53,8 @@ export class ValidatorsMethod extends BaseMethod {
generatorKey: Buffer,
proofOfPossession: Buffer,
): Promise<void> {
if (validatorAddress.length !== ADDRESS_LENGTH) {
throw new Error(`Validator address must be ${ADDRESS_LENGTH} bytes long.`);
}
this._validateLengths({ validatorAddress, blsKey, proofOfPossession });

const validatorAccount = {
generatorKey,
blsKey,
Expand Down Expand Up @@ -103,9 +110,8 @@ export class ValidatorsMethod extends BaseMethod {
validatorAddress: Buffer,
generatorKey: Buffer,
): Promise<boolean> {
if (validatorAddress.length !== ADDRESS_LENGTH) {
throw new Error(`Validator address must be ${ADDRESS_LENGTH} bytes long.`);
}
this._validateLengths({ validatorAddress });

const validatorAccount = {
generatorKey,
blsKey: INVALID_BLS_KEY,
Expand Down Expand Up @@ -137,9 +143,7 @@ export class ValidatorsMethod extends BaseMethod {
methodContext: MethodContext,
blsKey: Buffer,
): Promise<ValidatorAddress> {
if (blsKey.length !== BLS_PUBLIC_KEY_LENGTH) {
throw new Error(`BLS public key must be ${BLS_PUBLIC_KEY_LENGTH} bytes long.`);
}
this._validateLengths({ blsKey });

const blsKeysSubStore = this.stores.get(BLSKeyStore);
const blsKeyExists = await blsKeysSubStore.has(methodContext, blsKey);
Expand All @@ -157,9 +161,7 @@ export class ValidatorsMethod extends BaseMethod {
methodContext: ImmutableMethodContext,
address: Buffer,
): Promise<ValidatorKeys> {
if (address.length !== ADDRESS_LENGTH) {
throw new Error(`Validator address length must be ${ADDRESS_LENGTH}.`);
}
this._validateLengths({ validatorAddress: address });

const validatorsSubStore = this.stores.get(ValidatorKeysStore);
const addressExists = await validatorsSubStore.has(methodContext, address);
Expand All @@ -176,12 +178,7 @@ export class ValidatorsMethod extends BaseMethod {
blsKey: Buffer,
proofOfPossession: Buffer,
): Promise<boolean> {
if (validatorAddress.length !== ADDRESS_LENGTH) {
throw new Error(`Validator address must be ${ADDRESS_LENGTH} bytes long.`);
}
if (blsKey.length !== BLS_PUBLIC_KEY_LENGTH) {
throw new Error(`BLS public key must be ${BLS_PUBLIC_KEY_LENGTH} bytes long.`);
}
this._validateLengths({ validatorAddress, blsKey, proofOfPossession });

const validatorsSubStore = this.stores.get(ValidatorKeysStore);
const addressExists = await validatorsSubStore.has(methodContext, validatorAddress);
Expand Down Expand Up @@ -244,12 +241,7 @@ export class ValidatorsMethod extends BaseMethod {
validatorAddress: Buffer,
generatorKey: Buffer,
): Promise<boolean> {
if (validatorAddress.length !== ADDRESS_LENGTH) {
throw new Error(`Validator address must be ${ADDRESS_LENGTH} bytes long.`);
}
if (generatorKey.length !== ED25519_PUBLIC_KEY_LENGTH) {
throw new Error(`Generator key must be ${ED25519_PUBLIC_KEY_LENGTH} bytes long.`);
}
this._validateLengths({ validatorAddress, generatorKey });

const validatorsSubStore = this.stores.get(ValidatorKeysStore);

Expand Down Expand Up @@ -362,4 +354,19 @@ export class ValidatorsMethod extends BaseMethod {
});
validatorSetter.setNextValidators(preCommitThreshold, certificateThreshold, validatorsWithKey);
}

private _validateLengths(args: ValidatorArgs) {
if (args.validatorAddress && args.validatorAddress.length !== ADDRESS_LENGTH) {
throw new Error(`Validator address must be ${ADDRESS_LENGTH} bytes long.`);
}
if (args.blsKey && args.blsKey.length !== BLS_PUBLIC_KEY_LENGTH) {
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.`);
}
if (args.generatorKey && args.generatorKey.length !== ED25519_PUBLIC_KEY_LENGTH) {
throw new Error(`Generator key must be ${ED25519_PUBLIC_KEY_LENGTH} bytes long.`);
}
}
}
6 changes: 6 additions & 0 deletions framework/src/modules/validators/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* Removal or modification of this copyright notice is prohibited.
*/

import { BLS_POP_LENGTH, BLS_PUBLIC_KEY_LENGTH } from './constants';

export interface ValidateBLSKeyRequest {
proofOfPossession: string;
blsKey: string;
Expand All @@ -25,10 +27,14 @@ export const validateBLSKeyRequestSchema = {
proofOfPossession: {
type: 'string',
format: 'hex',
minLength: BLS_POP_LENGTH * 2,
maxLength: BLS_POP_LENGTH * 2,
},
blsKey: {
type: 'string',
format: 'hex',
minLength: BLS_PUBLIC_KEY_LENGTH * 2,
maxLength: BLS_PUBLIC_KEY_LENGTH * 2,
},
},
required: ['proofOfPossession', 'blsKey'],
Expand Down
54 changes: 29 additions & 25 deletions framework/test/unit/modules/validators/endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ import { PrefixedStateReadWriter } from '../../../../src/state_machine/prefixed_
import { createTransientModuleEndpointContext } from '../../../../src/testing';
import { InMemoryPrefixedStateDB } from '../../../../src/testing/in_memory_prefixed_state';
import { createStoreGetter } from '../../../../src/testing/utils';
import {
ADDRESS_LENGTH,
BLS_POP_LENGTH,
BLS_PUBLIC_KEY_LENGTH,
ED25519_PUBLIC_KEY_LENGTH,
} from '../../../../src/modules/validators/constants';

describe('ValidatorsModuleEndpoint', () => {
let validatorsModule: ValidatorsModule;
let stateStore: PrefixedStateReadWriter;
const pk = utils.getRandomBytes(48);
const address = utils.getRandomBytes(48);
const proof = utils.getRandomBytes(96);
const validatorAddress = utils.getRandomBytes(20);
const blsKey = utils.getRandomBytes(48);
const generatorKey = utils.getRandomBytes(32);
const proof = utils.getRandomBytes(BLS_POP_LENGTH);
const validatorAddress = utils.getRandomBytes(ADDRESS_LENGTH);
const blsKey = utils.getRandomBytes(BLS_PUBLIC_KEY_LENGTH);
const generatorKey = utils.getRandomBytes(ED25519_PUBLIC_KEY_LENGTH);
const validProof =
'88bb31b27eae23038e14f9d9d1b628a39f5881b5278c3c6f0249f81ba0deb1f68aa5f8847854d6554051aa810fdf1cdb02df4af7a5647b1aa4afb60ec6d446ee17af24a8a50876ffdaf9bf475038ec5f8ebeda1c1c6a3220293e23b13a9a5d26';

Expand All @@ -46,13 +50,13 @@ describe('ValidatorsModuleEndpoint', () => {
stateStore,
params: {
proofOfPossession: proof.toString('hex'),
blsKey: pk.toString('hex'),
blsKey: blsKey.toString('hex'),
},
});

await validatorsModule.stores
.get(BLSKeyStore)
.set(createStoreGetter(stateStore), pk, { address });
await validatorsModule.stores.get(BLSKeyStore).set(createStoreGetter(stateStore), blsKey, {
address: utils.getRandomBytes(ADDRESS_LENGTH),
});

await expect(validatorsModule.endpoint.validateBLSKey(context)).resolves.toStrictEqual({
valid: false,
Expand All @@ -64,7 +68,7 @@ describe('ValidatorsModuleEndpoint', () => {
stateStore,
params: {
proofOfPossession: proof.toString('hex'),
blsKey: pk.toString('hex'),
blsKey: blsKey.toString('hex'),
},
});
await expect(validatorsModule.endpoint.validateBLSKey(context)).resolves.toStrictEqual({
Expand All @@ -87,48 +91,48 @@ describe('ValidatorsModuleEndpoint', () => {
});

it('should resolve with false when proof of possession is invalid but bls key has a valid length', async () => {
const validPk =
const validBLSKey =
'a491d1b0ecd9bb917989f0e74f0dea0422eac4a873e5e2644f368dffb9a6e20fd6e10c1b77654d067c0618f6e5a7f79a';
const invalidProof =
'b803eb0ed93ea10224a73b6b9c725796be9f5fefd215ef7a5b97234cc956cf6870db6127b7e4d824ec62276078e787db05584ce1adbf076bc0808ca0f15b73d59060254b25393d95dfc7abe3cda566842aaedf50bbb062aae1bbb6ef3b1fffff';
const context = createTransientModuleEndpointContext({
stateStore,
params: {
proofOfPossession: invalidProof,
blsKey: validPk,
blsKey: validBLSKey,
},
});
await expect(validatorsModule.endpoint.validateBLSKey(context)).resolves.toStrictEqual({
valid: false,
});
});

it('should resolve with false when bls key length is less than 48 bytes and proof of possession has valid length', async () => {
const invalidPk = utils.getRandomBytes(47).toString('hex');
it('should throw when BLS key length is too short and proof of possession has valid length', async () => {
const shortBLSKey = utils.getRandomBytes(BLS_PUBLIC_KEY_LENGTH - 1).toString('hex');
const context = createTransientModuleEndpointContext({
stateStore,
params: {
proofOfPossession: validProof,
blsKey: invalidPk,
blsKey: shortBLSKey,
},
});
await expect(validatorsModule.endpoint.validateBLSKey(context)).resolves.toStrictEqual({
valid: false,
});
await expect(validatorsModule.endpoint.validateBLSKey(context)).rejects.toThrow(
`Property '.blsKey' must NOT have fewer than ${BLS_PUBLIC_KEY_LENGTH * 2} characters`,
);
});

it('should resolve with false when bls key length is greater than 48 bytes and proof of possession has valid length', async () => {
const invalidPk = utils.getRandomBytes(49).toString('hex');
it('should throw when BLS key length is too long and proof of possession has valid length', async () => {
const longBLSKey = utils.getRandomBytes(BLS_PUBLIC_KEY_LENGTH + 1).toString('hex');
const context = createTransientModuleEndpointContext({
stateStore,
params: {
proofOfPossession: validProof,
blsKey: invalidPk,
blsKey: longBLSKey,
},
});
await expect(validatorsModule.endpoint.validateBLSKey(context)).resolves.toStrictEqual({
valid: false,
});
await expect(validatorsModule.endpoint.validateBLSKey(context)).rejects.toThrow(
`Property '.blsKey' must NOT have more than ${BLS_PUBLIC_KEY_LENGTH * 2} characters`,
);
});
});

Expand Down
Loading