-
Notifications
You must be signed in to change notification settings - Fork 45
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!: updates the HashNode and HashLeaf methods to return error instead of panic and refactors the code #136
Conversation
Codecov Report
@@ Coverage Diff @@
## master #136 +/- ##
==========================================
- Coverage 96.33% 96.20% -0.14%
==========================================
Files 6 6
Lines 464 527 +63
==========================================
+ Hits 447 507 +60
- Misses 11 12 +1
- Partials 6 8 +2
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
This PR is ready for review, with the exception of the code coverage percentage, which is currently 0.14% below the threshold. This |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No blocking feedback, great work!
hasher_test.go
Outdated
ErrInvalidLeafLen, | ||
}, | ||
{ | ||
"invalid node: left.max > right.imn", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"invalid node: left.max > right.imn", | |
"invalid node: left.max > right.min", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed 7dfb628
nmt_test.go
Outdated
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
_, err := tt.tree.Root() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] should this invoke computeRoot? It currently seems identical to the above test:
func Test_Root_Error(t *testing.T) {
// ...
_, err := tt.tree.Root()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for finding this, please see the corrected version in this commit 3fd5dab
gotLeafHashes = append(gotLeafHashes, hashLeafFunc(leafData)) | ||
leafHash, err := hashLeafFunc(gotLeaf) | ||
if err != nil { // this can never happen due to the initial validation of the leaf at the beginning of the loop | ||
return false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[optional] if this can never happen, should it panic
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason it cannot happen is that the leaf is validated earlier in the code. However, if that section of the code changes in the future, we may encounter an error at this line. If the leaf is found to be invalid, we should return false instead of panicking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you check the line where the leaf gets validated, you will see that we return false. So, the same behaviour is replicated here.
hashes[i] = nth.HashLeaf(leafData) | ||
res, err := nth.HashLeaf(leafData) | ||
if err != nil { | ||
return false // this never can happen since the leafData is guaranteed to be namespaced |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] given this should never happen, should it panic
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same argument as in this also applies in here.
proof_test.go
Outdated
// proof4 is the inclusion proof for the leaf at index 3 | ||
leaf4WONamespace := nmt.leaves[3][nmt.NamespaceSize():] // the VerifyInclusion function expects the leaf without the namespace ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] what does the variable name here mean? It appears to truncate some leading bytes from the leaf so maybe leaf 4 without namespace? If so, proposal to rename
// proof4 is the inclusion proof for the leaf at index 3 | |
leaf4WONamespace := nmt.leaves[3][nmt.NamespaceSize():] // the VerifyInclusion function expects the leaf without the namespace ID | |
// proof4 is the inclusion proof for the leaf at index 3 | |
leaf4WithoutNamespace := nmt.leaves[3][nmt.NamespaceSize():] // the VerifyInclusion function expects the leaf without the namespace ID |
[question] if VerifyInclusion expects the leaf without the namespace, why does this tampered test data fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] what does the variable name here mean? It appears to truncate some leading bytes from the leaf so maybe leaf 4 without namespace? If so, proposal to rename
In this variable name i.e., leaf4WONamespace
, the WO
part is the abbreviation of without
, however, it looks like unclear (since you asked), so I'm going to change it to the full version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] if VerifyInclusion expects the leaf without the namespace, why does this tampered test data fail?
The reason for the function's failure is not related to the structure of leaf4WithoutNamespace
. The format of leaf4WithoutNamespace
is correct, and in the line you referred to, I trimmed the namespace to ensure it is a valid input for the function. The error is caused by the corrupted proof.nodes
section that follows that line. The node length is cut to nmt.NamespaceSize()
bytes, resulting in the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] what does the variable name here mean? It appears to truncate some leading bytes from the leaf so maybe leaf 4 without namespace? If so, proposal to rename
Please see the new variable name in 3f1b523
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only one question atm, but will continue to review
if err != nil { | ||
panic(err) // this should never happen since the data is already validated in the Write method | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question]
could this still be hit when verifying a proof since we're not calling the write method ahead of time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Write
method is not called prior to the Sum
, then the code panics due to the absence of the tp
field of the Hasher
. More details follow:
For the Sum
to get through, the tp
field of the hasher should be set to one of the LeafPrefix
or NodePrefix
based on which the hasher determines whether the input data belongs to a leaf or a non-leaf node. Without making call to the Write
method, the Hasher.tp
field remains unset, hence in the mentioned switch statement, none of the LeafPrefix
and NodePrefix
cases are true, and the code goes straight to the default
part which panics due to unset tp
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another point is that, it is not possible to call Sum
without making a call to the Write
method, as it corresponds to doing nothing (the Hasher
state is empty). Doing so always results in panic due to undefined state
Line 121 in 85ec3c9
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
benchstat results (Update: this is no longer needed as the gobencher has been fixed now):
|
Overview
Closes #109 and #99.
This pull request ensures that the
HashNode
andHashLeaf
functions return errors instead of panicking, and that caller functions properly handle any emitted errors. When generating proofs, any hashing errors are propagated with additional context. During verification, hashing errors are treated as unsuccessful proof verification.Note that in this PR, the term
Irrecoverable errors
used to describe errors that any number of retries can correct the them.Checklist