Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: btclightclient: Refactor keepers based on needs of btcheckpoint module #72

Merged
merged 4 commits into from
Jul 27, 2022

Conversation

vitsalis
Copy link
Member

This PR directly relates to the changes introduced in #70 . More specifically, we refactor some of the keepers in order to have the expected parameters and add new keepers with the requested functionality. New tests are introduced and refactored accordingly.

@KonradStaniec please verify whether this is the expected behavior. There are no major updates in the MainChainDepth method since this matter is still under discussion at #70 (see discussion). Once this is resolved, this will be finalized as well.

// Returns false if the parent and the child are the same header.
func (k Keeper) IsAncestor(ctx sdk.Context, parentHashBytes *bbl.BTCHeaderHashBytes, childHashBytes *bbl.BTCHeaderHashBytes) (bool, error) {
// nil checks
if parentHashBytes == nil || childHashBytes == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment to arguments, why using passing by pointer not by value ? In general using pointer should be used when passing larger structs to avoid copying or when arguments are optional. Imo both are not true here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do this mostly out of convenience. Due to the usage of the customtype attribute on the proto files, most elements are pointers to BTCHeaderBytes or BTCHeaderHashBytes objects. If the parameters are not passed as pointers, then we would have a bunch of pointer dereferences in various places and extra checks, so I prefer to delegate those checks to when the elements are actually getting used.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so I understand this convenience argument although not fully agree with it as:

  • using IsAncestor with nil hashes does not really have any semantic meaning.
  • IsAncestor does not have full context of the caller if nil pointer is really error or rather a panic for example, when receiving input form external source like lets say json rpc this is really an error , but retrieving this hash from our local database it should rather be panic as we should not save nil stuf in our database. (unelss of course we use pointer to represent optional value)

Nevertheless as was merged first I will adapt my pr to use pointers, so that interfaces will allign,

Comment on lines 225 to 228
// If they have the same height, then the result is false
if childHeader.Height == parentHeader.Height {
return false, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious why you don't consider this to be an error as well, or the why isn't the previous not an error if this one isn't.

It sounds like either the caller should know that the block behind parentHashBytes can be an ancestor of the one behind childHashBytes, or they don't. If they know that it should be okay to call this because the height is <, then they should also know not to call when the height is ==. OTOH if they don't know, then both should not be considered errors, just plain false.

if err != nil {
t.Errorf("Valid input led to an error")
}
if isAncestor != tree.IsOnNodeChain(header, ancestor) { // The result should be whether it is an ancestor or not
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For completeness sake I will say it it again: instead of having an algorithm to check, you should be able to generate data knowing what the outcome can be by picking a random ancestor and extending with another tree. Then you can pick any node in the extension and it's a descendant. The only question is is whether you can easily pick a node that doesn't have other descendants in the original tree.

But if you want to keep it this way that's fine as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I agree. In this case, we can get an ancestor or a descendant through the usage of the tree.RandomAncestor() and tree.RandomDescendant() methods, so extra tree building is not required.

However, in this test I follow this approach in order to get two birds with one stone. Specifically, I want to test that the function returns true if the node is an anecstor and false otherwise. Randomness ensures that both the test cases are going to be tested. I don't see any difference on using the IsOnNodeChain compared to using the RandomAncestor() or the RandomDescendant functions that are used extensivelly throughout the tests and testing the two cases respectively.

Copy link
Contributor

@aakoshh aakoshh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome :)

@vitsalis vitsalis merged commit ef33936 into main Jul 27, 2022
@vitsalis vitsalis deleted the btclightclient-expected-keepers branch July 27, 2022 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants