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

Unit test review: BFT #7957

Closed
Tracked by #7244
janhack opened this issue Jan 5, 2023 · 4 comments · Fixed by #8422
Closed
Tracked by #7244

Unit test review: BFT #7957

janhack opened this issue Jan 5, 2023 · 4 comments · Fixed by #8422
Assignees
Milestone

Comments

@janhack
Copy link

janhack commented Jan 5, 2023

bft_processing.spec.ts

  1. In this case, it should not make a difference, but generally it would be good to avoid floor and ceil on floating point numbers and instead only use integer arithmetic. Concretely, Math.floor((scenario.config.activeValidators * 2) / 3) + 1; could be replaced with integer division.
  2. I don't understand the cases expect(bftInfo?.prevoteWeight).toBeUndefined(); and expect(bftInfo?.precommitWeight).toBeUndefined();. I am not sure why why expect the weight to be undefined. Isn't it the case that the bftInfo array does not contain an object at that height in the first place.
  3. I assume this file runs all the tests generated from the CSV files in the bft_processing folder . As I checked that they are unchanged and I reviewed them in the past, I did not review them again.

bft_votes.spec.ts

  1. The state of the BFT votes is not consistent. maxHeightPrevoted == 103, but the block at height 149 has prevote weight 68. This can happen, of course, after calling updatePrevotesPrecommits and before then calling updateMaxHeightPrevoted. But in the BFT protocol no other function is called in between those two functions. Probably that does not create an issue with the test, but in principle it could have undesired side-effects and it would be best to test with a consistent state of BFT votes.
  2. 'should not stake on blocks if generator is not in the validators': It shoud still be called vote in this case, as these are BFT votes that have nothing to do with DPoS.
  3. 'should not increment the prevote when generator is not active': The provided block with maxHeightGenerated == 0 would not be added to the chain as it contradicts the block at height 149 by the same generator. Maybe this is still fine for the tests, but it could be better to have a valid block.
  4. 'should not update maxHeightPrevoted if no block info exceeds threshold': I think it would be better to test prevoteThreshold: BigInt(69), i.e., exactly just increment the minimal amount to get a different result, instead of choosing an extreme.
  5. 'should store maximum height where prevote exceeds threshold': You mean precommit here, I think. At least this is what is tested.
  6. 'should not update maxHeightPrevoted if no block info exceeds threshold': Same as for point 6 here.
  7. 'should not update maxHeightCertified when aggregateCommit is empty': The test is correct, but note that in that case, the block should be invalid as we should have aggregateCommit.height == bftVotes.maxHeightCertified. This validation is not defined in LIP 58, but part of the aggregate commit validation.

method.spec.ts

  1. areHeadersContradicting: These tests only cover the easy cases, but not the cases depending on maxHeightPrevoted and maxHeightGenerated. I guess we assume here those tests are covered by the function areDistinctHeadersContradicting.
  2. isHeaderContradictingChain: The initialization maxHeightPrevoted: 10, does not make sense. This probably does not affect the test.
  3. 'getBFTParameters': I think it would be nice to also test the return value for heights 29 and 30.
  4. 'getNextHeightBFTParameters': It would be good to test that for input 19 the function returns 20.
  5. 'should store BFT parameters with height maxHeightPrevoted + 1 if blockBFTInfo does not exist': The BFT parameters should be stored at genesisHeight + 1 in this case (note that only after processing the genesis block, blockBFTInfo should be empty and then we actually have maxHeightPrevoted == genesisHeight).

utils.spec.ts

  1. 'areDistinctHeadersContradicting': In some cases, we do not define maxHeightGenerated or maxHeightPrevoted for the block headers (I assume they are then just some random value). I think it would be better to also define those, to be sure that the headers are not considered contradicting for other reasons. Currently, it is also difficult to see that all cases in the function are covered.

General comment

  1. All the test cases seem with uniform BFT weights of 1. It would be nice to use general BFT weights, i.e., arbitrary integers as BFT weights, to check these are also handled correctly.
  2. There is no test currently for the functions getGeneratorAtTimestamp and getSlotNumber. They are maybe not directly part of the BFT protocol, but are defined in LIP 0058.
@janhack janhack self-assigned this Jan 5, 2023
@janhack janhack changed the title Unit test review: BFT Unit test review: BFT (in progress) Jan 5, 2023
@Madhulearn Madhulearn added this to the Sprint 94 milestone Apr 24, 2023
@shuse2 shuse2 changed the title Unit test review: BFT (in progress) Unit test review: BFT May 2, 2023
@shuse2
Copy link
Collaborator

shuse2 commented May 2, 2023

In this case, it should not make a difference, but generally it would be good to avoid floor and ceil on floating point numbers and instead only use integer arithmetic. Concretely, Math.floor((scenario.config.activeValidators * 2) / 3) + 1; could be replaced with integer division.

As far as the research by @AndreasKendziorra , the division is deterministic although there will be an rounding error when using floating number

I don't understand the cases expect(bftInfo?.prevoteWeight).toBeUndefined(); and expect(bftInfo?.precommitWeight).toBeUndefined();. I am not sure why why expect the weight to be undefined. Isn't it the case that the bftInfo array does not contain an object at that height in the first place.

This is because blockBFTInfo will be removed with batch size * 3 while inserting a block. although it exists in the fixture, the below min height, the value should be undefined

areHeadersContradicting: These tests only cover the easy cases, but not the cases depending on maxHeightPrevoted and maxHeightGenerated. I guess we assume here those tests are covered by the function areDistinctHeadersContradicting.

yes. those should be covered in areDistinctHeadersContradicting.

'should store BFT parameters with height maxHeightPrevoted + 1 if blockBFTInfo does not exist': The BFT parameters should be stored at genesisHeight + 1 in this case (note that only after processing the genesis block, blockBFTInfo should be empty and then we actually have maxHeightPrevoted == genesisHeight).

I think in the BFT, it is not considering genesis height. the logic is if the bft info is empty, store maxHeightPrevoted + 1 which only happens in genesis block case?

@Madhulearn Madhulearn modified the milestones: Sprint 94, Sprint 95 May 9, 2023
@janhack
Copy link
Author

janhack commented May 12, 2023

I think in the BFT, it is not considering genesis height. the logic is if the bft info is empty, store maxHeightPrevoted + 1 which only happens in genesis block case?

No, in this case the genesis height is used, see the function getCurrentHeight in https://github.com/LiskHQ/lips/blob/main/proposals/lip-0058.md#getcurrentheight

@shuse2
Copy link
Collaborator

shuse2 commented May 12, 2023

No, in this case the genesis height is used, see the function getCurrentHeight in https://github.com/LiskHQ/lips/blob/main/proposals/lip-0058.md#getcurrentheight

I think we missed to update this, ill try to address it in different PR

@shuse2
Copy link
Collaborator

shuse2 commented May 12, 2023

I was double checking and currently we store genesis height as maxHeightPrevoted in https://github.com/LiskHQ/lisk-sdk/blob/development/framework/src/engine/bft/module.ts#L49-L60

Therefore, when we use in the empty case https://github.com/LiskHQ/lisk-sdk/blob/development/framework/src/engine/bft/method.ts#L222, the maxHeightPrevoted is genesis height

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants