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

Used hash onion parameters count & 'height' not updated if imported from another node and block height is less than 'height' #8429

Merged
merged 3 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 10 additions & 0 deletions framework/src/modules/random/stores/used_hash_onions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ export class UsedHashOnionsStore extends BaseOffchainStore<UsedHashOnionStoreObj

if (index === -1) {
originalUsedHashOnions.push(usedHashOnion);
// if newly inserted entry has count 0, then cleanup previous entries
if (usedHashOnion.count === 0) {
const newUsedHashonions = [usedHashOnion];
Incede marked this conversation as resolved.
Show resolved Hide resolved
await this.set(
ctx,
address,
this._filterUsedHashOnions(newUsedHashonions, finalizedHeight),
);
return;
}
bobanm marked this conversation as resolved.
Show resolved Hide resolved
} else {
// eslint-disable-next-line no-param-reassign
originalUsedHashOnions[index] = usedHashOnion;
Expand Down
187 changes: 179 additions & 8 deletions framework/test/unit/modules/random/module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ describe('RandomModule', () => {

const targetValidatorAddress = address.getAddressFromLisk32Address(targetValidator.address);

it('should assign seed reveal to block header asset', async () => {
it('should assign seed reveal to block header asset and update the used hash onion', async () => {
// Arrange
const blockGenerateContext: InsertAssetContext = testing.createBlockGenerateContext({
assets: assetStub,
Expand Down Expand Up @@ -304,8 +304,24 @@ describe('RandomModule', () => {
).resolves.toEqual(defaultUsedHashOnionUpdated);
});

it('should update the used hash onion', async () => {
it('should overwrite the used hash onion when forging the same height', async () => {
// Arrange
const usedHashOnionInput: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 5,
height: 9,
},
{
count: 6,
height: 12,
},
{
count: 7,
height: 15,
},
],
};
bobanm marked this conversation as resolved.
Show resolved Hide resolved

const blockGenerateContext: InsertAssetContext = testing.createBlockGenerateContext({
assets: assetStub,
Expand All @@ -316,10 +332,9 @@ describe('RandomModule', () => {
getStore: jest.fn() as any,
header: { height: 15, generatorAddress: targetValidatorAddress } as any,
});

await randomModule.offchainStores
.get(UsedHashOnionsStore)
.set(blockGenerateContext, targetValidatorAddress, defaultUsedHashOnion);
.set(blockGenerateContext, targetValidatorAddress, usedHashOnionInput);

const seed = targetValidator.hashOnion.hashes[1];
const hashes = utils.hashOnion(
Expand Down Expand Up @@ -348,22 +363,178 @@ describe('RandomModule', () => {
).resolves.toEqual(defaultUsedHashOnionUpdated);
});

it('should overwrite the used hash onion when forging the same height', async () => {
it('should remove other used hash onions except count 0 at current height if all entries in the used hash onion array have height greater than the current block height', async () => {
// Arrange
// when all entries have height more than the current block height, an entry with count 0 will be initialzed in the used hash onions array
const usedHashOnionInput: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 5,
height: 9,
height: 17,
},
{
count: 6,
height: 21,
},
{
count: 7,
height: 25,
},
],
};
// all entries except count 0 at current block height removed
const usedHashOnionUpdated: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 0,
height: 15,
},
],
};

const blockGenerateContext: InsertAssetContext = testing.createBlockGenerateContext({
assets: assetStub,
logger: testing.mocks.loggerMock,
chainID: defaultChainID,
getOffchainStore: (p1, p2) => offchainStore.getStore(p1, p2),
getMethodContext: jest.fn() as any,
getStore: jest.fn() as any,
header: { height: 15, generatorAddress: targetValidatorAddress } as any,
});
await randomModule.offchainStores
.get(UsedHashOnionsStore)
.set(blockGenerateContext, targetValidatorAddress, usedHashOnionInput);

const seed = targetValidator.hashOnion.hashes[1];
const hashes = utils.hashOnion(
Buffer.from(seed, 'hex'),
targetValidator.hashOnion.distance,
1,
);

// Act
await randomModule.init({
genesisConfig: {} as GenesisConfig,
moduleConfig: {},
});
await randomModule.insertAssets(blockGenerateContext);

// Assert
expect(assetStub.setAsset).toHaveBeenCalledTimes(1);
expect(assetStub.setAsset).toHaveBeenCalledWith(
randomModule.name,
codec.encode(blockHeaderAssetRandomModule, { seedReveal: hashes[0] }),
);
await expect(
randomModule.offchainStores
.get(UsedHashOnionsStore)
.get(blockGenerateContext, targetValidatorAddress),
).resolves.toEqual(usedHashOnionUpdated);
});

it('should remove other used hash onions except count 0 at current height if there is no entry in the used hash onion array with height less than the current block height', async () => {
// Arrange
// when all entries have height more than or equal to the current block height, an entry with count 0 will be initialzed in the used hash onions array
const usedHashOnionInput: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 5,
height: 15,
},
{
count: 6,
height: 21,
},
{
count: 7,
height: 25,
},
],
};
// all entries except count 0 at current block height removed
const usedHashOnionUpdated: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 0,
height: 15,
},
],
};

const blockGenerateContext: InsertAssetContext = testing.createBlockGenerateContext({
assets: assetStub,
logger: testing.mocks.loggerMock,
chainID: defaultChainID,
getOffchainStore: (p1, p2) => offchainStore.getStore(p1, p2),
getMethodContext: jest.fn() as any,
getStore: jest.fn() as any,
header: { height: 15, generatorAddress: targetValidatorAddress } as any,
});
await randomModule.offchainStores
.get(UsedHashOnionsStore)
.set(blockGenerateContext, targetValidatorAddress, usedHashOnionInput);

const seed = targetValidator.hashOnion.hashes[1];
const hashes = utils.hashOnion(
Buffer.from(seed, 'hex'),
targetValidator.hashOnion.distance,
1,
);

// Act
await randomModule.init({
genesisConfig: {} as GenesisConfig,
moduleConfig: {},
});
await randomModule.insertAssets(blockGenerateContext);

// Assert
expect(assetStub.setAsset).toHaveBeenCalledTimes(1);
expect(assetStub.setAsset).toHaveBeenCalledWith(
randomModule.name,
codec.encode(blockHeaderAssetRandomModule, { seedReveal: hashes[0] }),
);
await expect(
randomModule.offchainStores
.get(UsedHashOnionsStore)
.get(blockGenerateContext, targetValidatorAddress),
).resolves.toEqual(usedHashOnionUpdated);
});

it('should not remove other used hash onions if there is atleast one entry in the used hash onion array with height less than the current block height', async () => {
// Arrange
// when there is atleast one entry with height less than or equal to the current block height, no entry with count 0 will be initialzed in the used hash onions array
const usedHashOnionInput: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 5,
height: 12,
},
{
count: 6,
height: 21,
},
{
count: 7,
height: 25,
},
],
};
// no entry removed and current height updated
const usedHashOnionUpdated: UsedHashOnionStoreObject = {
usedHashOnions: [
{
count: 5,
height: 12,
},
{
count: 6,
height: 15,
},
{
count: 7,
height: 25,
},
],
};

Expand Down Expand Up @@ -398,13 +569,13 @@ describe('RandomModule', () => {
expect(assetStub.setAsset).toHaveBeenCalledTimes(1);
expect(assetStub.setAsset).toHaveBeenCalledWith(
randomModule.name,
codec.encode(blockHeaderAssetRandomModule, { seedReveal: hashes[7] }),
codec.encode(blockHeaderAssetRandomModule, { seedReveal: hashes[6] }),
);
await expect(
randomModule.offchainStores
.get(UsedHashOnionsStore)
.get(blockGenerateContext, targetValidatorAddress),
).resolves.toEqual(defaultUsedHashOnionUpdated);
).resolves.toEqual(usedHashOnionUpdated);
});

it('should remove all used hash onions before finality height', async () => {
Expand Down