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

Commit

Permalink
Cleanup if there is an entry with count zero
Browse files Browse the repository at this point in the history
  • Loading branch information
Incede committed May 8, 2023
1 parent 9e1da1d commit 1a42e7c
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 8 deletions.
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];
await this.set(
ctx,
address,
this._filterUsedHashOnions(newUsedHashonions, finalizedHeight),
);
return;
}
} 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,
},
],
};

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

0 comments on commit 1a42e7c

Please sign in to comment.