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

Commit

Permalink
Validate lengths of arguments used in Validator module methods and en…
Browse files Browse the repository at this point in the history
…dpoints (#8920)

* Validate lengths of arguments used in Validator module methods and endpoints

* Fix format issue
  • Loading branch information
bobanm authored Sep 1, 2023
1 parent 108b1ce commit fbf48ba
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 68 deletions.
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

0 comments on commit fbf48ba

Please sign in to comment.