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

Update random module endpoints getHashOnionUsage and setHashOnionUsage #8431

Merged
merged 4 commits into from
May 15, 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
31 changes: 19 additions & 12 deletions framework/src/modules/random/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import { validator } from '@liskhq/lisk-validator';
import * as cryptography from '@liskhq/lisk-cryptography';
import { NotFoundError } from '@liskhq/lisk-db';
import { ModuleEndpointContext } from '../../types';
import { BaseEndpoint } from '../base_endpoint';
import { ADDRESS_LENGTH, EMPTY_KEY } from './constants';
Expand All @@ -33,7 +34,7 @@ import {
import { ValidatorRevealsStore } from './stores/validator_reveals';
import { getSeedRevealValidity } from './utils';
import { HashOnionStore } from './stores/hash_onion';
import { UsedHashOnionsStore } from './stores/used_hash_onions';
import { UsedHashOnionStoreObject, UsedHashOnionsStore } from './stores/used_hash_onions';

export class RandomEndpoint extends BaseEndpoint {
public async isSeedRevealValid(ctx: ModuleEndpointContext): Promise<{ valid: boolean }> {
Expand Down Expand Up @@ -141,24 +142,30 @@ export class RandomEndpoint extends BaseEndpoint {
const seed = hashOnion.hashes[hashOnion.hashes.length - 1].toString('hex');

const usedHashOnionStore = this.offchainStores.get(UsedHashOnionsStore);
const usedHashOnion = await usedHashOnionStore.getLatest(ctx, address);
if (!usedHashOnion) {
return {
count: 0,
height: 0,
seed,
};

let usedHashOnion: UsedHashOnionStoreObject;
try {
usedHashOnion = await usedHashOnionStore.get(ctx, address);
} catch (error) {
if (error instanceof NotFoundError) {
return {
usedHashOnions: [{ count: 0, height: 0 }],
seed,
};
}
throw error;
}

return { height: usedHashOnion.height, count: usedHashOnion.count, seed };
return { usedHashOnions: usedHashOnion.usedHashOnions, seed };
}

public async setHashOnionUsage(ctx: ModuleEndpointContext): Promise<void> {
validator.validate<SetHashOnionUsageRequest>(setHashOnionUsageRequest, ctx.params);
const { count, height } = ctx.params;
const address = cryptography.address.getAddressFromLisk32Address(ctx.params.address);

const { address, usedHashOnions } = ctx.params;
const generatorAddress = cryptography.address.getAddressFromLisk32Address(address);

const usedHashOnionStore = this.offchainStores.get(UsedHashOnionsStore);
await usedHashOnionStore.set(ctx, address, { usedHashOnions: [{ count, height }] });
await usedHashOnionStore.set(ctx, generatorAddress, { usedHashOnions });
}
}
57 changes: 37 additions & 20 deletions framework/src/modules/random/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/

import { ADDRESS_LENGTH, SEED_LENGTH } from './constants';
import { UsedHashOnion } from './stores/used_hash_onions';

interface AddressRequest {
address: string;
Expand Down Expand Up @@ -94,8 +95,7 @@ export const hasHashOnionResponseSchema = {
};

export interface GetHashOnionUsageResponse {
height: number;
count: number;
readonly usedHashOnions: UsedHashOnion[];
seed: string;
}

Expand All @@ -104,15 +104,24 @@ export type GetHashOnionUsageRequest = AddressRequest;
export const getHashOnionUsageResponse = {
$id: 'lisk/random/getHashOnionUsageResponse',
type: 'object',
required: ['count', 'height', 'seed'],
required: ['usedHashOnions', 'seed'],
properties: {
count: {
type: 'integer',
format: 'uint32',
},
height: {
type: 'integer',
format: 'uint32',
usedHashOnions: {
type: 'array',
items: {
type: 'object',
required: ['count', 'height'],
properties: {
count: {
type: 'integer',
format: 'uint32',
},
height: {
type: 'integer',
format: 'uint32',
},
},
},
},
seed: {
type: 'string',
Expand All @@ -122,26 +131,34 @@ export const getHashOnionUsageResponse = {
};

export interface SetHashOnionUsageRequest extends AddressRequest {
height: number;
count: number;
usedHashOnions: UsedHashOnion[];
}

export const setHashOnionUsageRequest = {
$id: 'lisk/random/setHashOnionUsageRequest',
type: 'object',
required: ['address', 'count', 'height'],
required: ['address', 'usedHashOnions'],
properties: {
address: {
type: 'string',
format: 'lisk32',
},
count: {
type: 'integer',
minimum: 1,
},
height: {
type: 'integer',
format: 'uint32',
usedHashOnions: {
type: 'array',
items: {
type: 'object',
required: ['count', 'height'],
properties: {
count: {
type: 'integer',
format: 'uint32',
},
height: {
type: 'integer',
format: 'uint32',
},
},
},
},
},
};
Expand Down
80 changes: 38 additions & 42 deletions framework/test/unit/modules/random/endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import * as cryptography from '@liskhq/lisk-cryptography';
import { ModuleEndpointContext, RandomModule } from '../../../../src';
import { RandomEndpoint } from '../../../../src/modules/random/endpoint';
import { HashOnionStore } from '../../../../src/modules/random/stores/hash_onion';
import { UsedHashOnionsStore } from '../../../../src/modules/random/stores/used_hash_onions';
import {
UsedHashOnionStoreObject,
UsedHashOnionsStore,
} from '../../../../src/modules/random/stores/used_hash_onions';
import { ValidatorRevealsStore } from '../../../../src/modules/random/stores/validator_reveals';
import { PrefixedStateReadWriter } from '../../../../src/state_machine/prefixed_state_read_writer';
import { createTransientModuleEndpointContext } from '../../../../src/testing';
Expand Down Expand Up @@ -55,6 +58,22 @@ describe('RandomModuleEndpoint', () => {
];

const emptyBytes = Buffer.alloc(0);
const defaultUsedHashOnion: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 5,
height: 9,
},
{
count: 6,
height: 12,
},
{
count: 7,
height: 15,
},
],
};

beforeEach(async () => {
const randomModule = new RandomModule();
Expand Down Expand Up @@ -410,15 +429,15 @@ describe('RandomModuleEndpoint', () => {
});

describe('getHashOnionUsage', () => {
const seed = genesisValidators.validators[0].hashOnion.hashes[1];
const count = 1000;
const distance = 10;
let address: string;
let address2: string;

beforeEach(async () => {
// Arrange
address = genesisValidators.validators[0].address;
const seed = genesisValidators.validators[0].hashOnion.hashes[1];
const count = 1000;
const distance = 10;
address2 = genesisValidators.validators[1].address;

await randomEndpoint.setHashOnion({ ...context, params: { address, seed, count, distance } });
Expand All @@ -431,9 +450,7 @@ describe('RandomModuleEndpoint', () => {
await usedHashOnionStore.set(
context,
cryptography.address.getAddressFromLisk32Address(address),
{
usedHashOnions: [{ count: 20, height: 2121 }],
},
defaultUsedHashOnion,
);
});

Expand All @@ -453,8 +470,7 @@ describe('RandomModuleEndpoint', () => {

// Assert
expect(seedUsage).toEqual({
count: 20,
height: 2121,
usedHashOnions: defaultUsedHashOnion.usedHashOnions,
seed: genesisValidators.validators[0].hashOnion.hashes[1],
});
});
Expand All @@ -468,8 +484,7 @@ describe('RandomModuleEndpoint', () => {

// Assert
expect(seedUsage).toEqual({
count: 0,
height: 0,
usedHashOnions: [{ count: 0, height: 0 }],
seed: expect.any(String),
});
});
Expand All @@ -479,9 +494,7 @@ describe('RandomModuleEndpoint', () => {
it('should store the appropriate params in the offchain store', async () => {
// Arrange
const { address } = genesisValidators.validators[0];
const count = 1000;
const height = 50;
context.params = { address, count, height };
context.params = { address, usedHashOnions: defaultUsedHashOnion.usedHashOnions };

// Act
await randomEndpoint.setHashOnionUsage(context);
Expand All @@ -494,23 +507,21 @@ describe('RandomModuleEndpoint', () => {

// Assert
expect(usedOnionData).toEqual({
usedHashOnions: [
{
count: 1000,
height: 50,
},
],
usedHashOnions: defaultUsedHashOnion.usedHashOnions,
});
});

it('should throw error when address provided in params is invalid', async () => {
// Arrange
const address = ['address'];
const seed = genesisValidators.validators[0].hashOnion.hashes[1];
const count = 1000;
const distance = 1000;
const height = 50;
context.params = { address, seed, count, distance, height };
context.params = {
address,
seed,
distance,
usedHashOnions: defaultUsedHashOnion.usedHashOnions,
};

// Act & Assert
await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow(
Expand All @@ -525,11 +536,11 @@ describe('RandomModuleEndpoint', () => {
const count = 'count';
const distance = 1000;
const height = 50;
context.params = { address, seed, count, distance, height };
context.params = { address, seed, distance, usedHashOnions: [{ count, height }] };

// Act & Assert
await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow(
"Lisk validator found 1 error[s]:\nProperty '.count' should be of type 'integer'",
"Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.count' should be of type 'integer'\nProperty '.usedHashOnions.0.count' must match format \"uint32\"",
);
});

Expand All @@ -540,26 +551,11 @@ describe('RandomModuleEndpoint', () => {
const count = 1000;
const distance = 1000;
const height = 'height';
context.params = { address, seed, count, distance, height };
context.params = { address, seed, distance, usedHashOnions: [{ count, height }] };

// Act & Assert
await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow(
"Lisk validator found 2 error[s]:\nProperty '.height' should be of type 'integer'\nProperty '.height' must match format \"uint32\"",
);
});

it('should throw error when count is less than 1', async () => {
// Arrange
const { address } = genesisValidators.validators[0];
const seed = genesisValidators.validators[0].hashOnion.hashes[1];
const count = 0;
const distance = 1000;
const height = 50;
context.params = { address, seed, count, distance, height };

// Act & Assert
await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow(
'Lisk validator found 1 error[s]:\nmust be >= 1',
"Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.height' should be of type 'integer'\nProperty '.usedHashOnions.0.height' must match format \"uint32\"",
);
});
});
Expand Down