From 11fdb90c95ad29b92e14c24cecd21ceb6b3ea4eb Mon Sep 17 00:00:00 2001 From: Incede <33103370+Incede@users.noreply.github.com> Date: Sat, 6 May 2023 13:45:41 +0200 Subject: [PATCH 1/3] Update endpoints --- framework/src/modules/random/endpoint.ts | 14 ++-- framework/src/modules/random/schemas.ts | 62 +++++++++++------ .../test/unit/modules/random/endpoint.spec.ts | 67 +++++++++++-------- 3 files changed, 88 insertions(+), 55 deletions(-) diff --git a/framework/src/modules/random/endpoint.ts b/framework/src/modules/random/endpoint.ts index f6f4f5eeed2..4d72cc75f46 100644 --- a/framework/src/modules/random/endpoint.ts +++ b/framework/src/modules/random/endpoint.ts @@ -141,24 +141,24 @@ 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); + const usedHashOnion = await usedHashOnionStore.get(ctx, address); if (!usedHashOnion) { return { - count: 0, - height: 0, + usedHashOnions: [{ count: 0, height: 0 }], seed, }; } - return { height: usedHashOnion.height, count: usedHashOnion.count, seed }; + return { usedHashOnions: usedHashOnion.usedHashOnions, seed }; } public async setHashOnionUsage(ctx: ModuleEndpointContext): Promise { validator.validate(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 }); } } diff --git a/framework/src/modules/random/schemas.ts b/framework/src/modules/random/schemas.ts index c19bd71f4e7..58324e2fa7c 100644 --- a/framework/src/modules/random/schemas.ts +++ b/framework/src/modules/random/schemas.ts @@ -13,6 +13,7 @@ */ import { ADDRESS_LENGTH, SEED_LENGTH } from './constants'; +import { UsedHashOnion } from './stores/used_hash_onions'; interface AddressRequest { address: string; @@ -94,8 +95,7 @@ export const hasHashOnionResponseSchema = { }; export interface GetHashOnionUsageResponse { - height: number; - count: number; + readonly usedHashOnions: UsedHashOnion[]; seed: string; } @@ -104,44 +104,66 @@ 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', + fieldNumber: 1, + items: { + type: 'object', + required: ['count', 'height'], + properties: { + count: { + dataType: 'uint32', + fieldNumber: 1, + }, + height: { + dataType: 'uint32', + fieldNumber: 2, + }, + }, + }, }, seed: { type: 'string', format: 'hex', + fieldNumber: 2, }, }, }; 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', + fieldNumber: 1, }, - count: { - type: 'integer', - minimum: 1, - }, - height: { - type: 'integer', - format: 'uint32', + usedHashOnions: { + type: 'array', + fieldNumber: 2, + items: { + type: 'object', + required: ['count', 'height'], + properties: { + count: { + dataType: 'uint32', + fieldNumber: 1, + minimum: 1, + }, + height: { + dataType: 'uint32', + fieldNumber: 2, + }, + }, + }, }, }, }; diff --git a/framework/test/unit/modules/random/endpoint.spec.ts b/framework/test/unit/modules/random/endpoint.spec.ts index a0c8243bd12..c35f4a431fb 100644 --- a/framework/test/unit/modules/random/endpoint.spec.ts +++ b/framework/test/unit/modules/random/endpoint.spec.ts @@ -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'; @@ -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(); @@ -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 } }); @@ -431,9 +450,7 @@ describe('RandomModuleEndpoint', () => { await usedHashOnionStore.set( context, cryptography.address.getAddressFromLisk32Address(address), - { - usedHashOnions: [{ count: 20, height: 2121 }], - }, + defaultUsedHashOnion, ); }); @@ -453,8 +470,7 @@ describe('RandomModuleEndpoint', () => { // Assert expect(seedUsage).toEqual({ - count: 20, - height: 2121, + usedHashOnions: defaultUsedHashOnion.usedHashOnions, seed: genesisValidators.validators[0].hashOnion.hashes[1], }); }); @@ -468,8 +484,7 @@ describe('RandomModuleEndpoint', () => { // Assert expect(seedUsage).toEqual({ - count: 0, - height: 0, + usedHashOnions: [{ count: 0, height: 0 }], seed: expect.any(String), }); }); @@ -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); @@ -494,12 +507,7 @@ describe('RandomModuleEndpoint', () => { // Assert expect(usedOnionData).toEqual({ - usedHashOnions: [ - { - count: 1000, - height: 50, - }, - ], + usedHashOnions: defaultUsedHashOnion.usedHashOnions, }); }); @@ -507,10 +515,13 @@ describe('RandomModuleEndpoint', () => { // 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( @@ -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 1 error[s]:\nProperty \'.usedHashOnions.0.count\' should pass "dataType" keyword validation', ); }); @@ -540,11 +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\"", + 'Lisk validator found 1 error[s]:\nProperty \'.usedHashOnions.0.height\' should pass "dataType" keyword validation', ); }); @@ -555,7 +566,7 @@ describe('RandomModuleEndpoint', () => { const count = 0; 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( From 1e4eb7293d7cb2c12f06593533e338856649cb9d Mon Sep 17 00:00:00 2001 From: Incede <33103370+Incede@users.noreply.github.com> Date: Wed, 10 May 2023 03:27:49 +0200 Subject: [PATCH 2/3] feedback --- framework/src/modules/random/endpoint.ts | 21 ++++++++++++------- framework/src/modules/random/schemas.ts | 20 +++++++----------- .../test/unit/modules/random/endpoint.spec.ts | 4 ++-- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/framework/src/modules/random/endpoint.ts b/framework/src/modules/random/endpoint.ts index 4d72cc75f46..1ac354fddb1 100644 --- a/framework/src/modules/random/endpoint.ts +++ b/framework/src/modules/random/endpoint.ts @@ -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'; @@ -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 }> { @@ -141,12 +142,18 @@ 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.get(ctx, address); - if (!usedHashOnion) { - return { - usedHashOnions: [{ 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 { usedHashOnions: usedHashOnion.usedHashOnions, seed }; diff --git a/framework/src/modules/random/schemas.ts b/framework/src/modules/random/schemas.ts index 58324e2fa7c..6364d50112f 100644 --- a/framework/src/modules/random/schemas.ts +++ b/framework/src/modules/random/schemas.ts @@ -108,18 +108,17 @@ export const getHashOnionUsageResponse = { properties: { usedHashOnions: { type: 'array', - fieldNumber: 1, items: { type: 'object', required: ['count', 'height'], properties: { count: { - dataType: 'uint32', - fieldNumber: 1, + type: 'integer', + format: 'uint32', }, height: { - dataType: 'uint32', - fieldNumber: 2, + type: 'integer', + format: 'uint32', }, }, }, @@ -127,7 +126,6 @@ export const getHashOnionUsageResponse = { seed: { type: 'string', format: 'hex', - fieldNumber: 2, }, }, }; @@ -144,23 +142,21 @@ export const setHashOnionUsageRequest = { address: { type: 'string', format: 'lisk32', - fieldNumber: 1, }, usedHashOnions: { type: 'array', - fieldNumber: 2, items: { type: 'object', required: ['count', 'height'], properties: { count: { - dataType: 'uint32', - fieldNumber: 1, + type: 'integer', + format: 'uint32', minimum: 1, }, height: { - dataType: 'uint32', - fieldNumber: 2, + type: 'integer', + format: 'uint32', }, }, }, diff --git a/framework/test/unit/modules/random/endpoint.spec.ts b/framework/test/unit/modules/random/endpoint.spec.ts index c35f4a431fb..25abcbfa680 100644 --- a/framework/test/unit/modules/random/endpoint.spec.ts +++ b/framework/test/unit/modules/random/endpoint.spec.ts @@ -540,7 +540,7 @@ describe('RandomModuleEndpoint', () => { // Act & Assert await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow( - 'Lisk validator found 1 error[s]:\nProperty \'.usedHashOnions.0.count\' should pass "dataType" keyword validation', + "Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.count' should be of type 'integer'\nProperty '.usedHashOnions.0.count' must match format \"uint32\"", ); }); @@ -555,7 +555,7 @@ describe('RandomModuleEndpoint', () => { // Act & Assert await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow( - 'Lisk validator found 1 error[s]:\nProperty \'.usedHashOnions.0.height\' should pass "dataType" keyword validation', + "Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.height' should be of type 'integer'\nProperty '.usedHashOnions.0.height' must match format \"uint32\"", ); }); From 94a7221e80d1786144a15236bece2eff8f6f6a78 Mon Sep 17 00:00:00 2001 From: Incede <33103370+Incede@users.noreply.github.com> Date: Mon, 15 May 2023 17:29:32 +0200 Subject: [PATCH 3/3] Remove lower limit for count --- framework/src/modules/random/schemas.ts | 1 - .../test/unit/modules/random/endpoint.spec.ts | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/framework/src/modules/random/schemas.ts b/framework/src/modules/random/schemas.ts index 6364d50112f..33d2f41a9bd 100644 --- a/framework/src/modules/random/schemas.ts +++ b/framework/src/modules/random/schemas.ts @@ -152,7 +152,6 @@ export const setHashOnionUsageRequest = { count: { type: 'integer', format: 'uint32', - minimum: 1, }, height: { type: 'integer', diff --git a/framework/test/unit/modules/random/endpoint.spec.ts b/framework/test/unit/modules/random/endpoint.spec.ts index 25abcbfa680..c15f5169c0d 100644 --- a/framework/test/unit/modules/random/endpoint.spec.ts +++ b/framework/test/unit/modules/random/endpoint.spec.ts @@ -558,20 +558,5 @@ describe('RandomModuleEndpoint', () => { "Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.height' should be of type 'integer'\nProperty '.usedHashOnions.0.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, distance, usedHashOnions: [{ count, height }] }; - - // Act & Assert - await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow( - 'Lisk validator found 1 error[s]:\nmust be >= 1', - ); - }); }); });