-
Notifications
You must be signed in to change notification settings - Fork 395
Test for lineage.depth
overflow of the extended keys
#1624
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
Conversation
If I read the output of the CI correctly, it runs all tests together as a single test as part of the installation in |
My bad, thanks! The two failing tests are indeed the ones I was talking about. The offending lines is this and this one. The idea of the tests was to hit the overflow when going from 255 xpriv to 256/0 xpub. This code. Oh, wait a minute. Isn't the |
No worries, in some of the CI outputs (CMake I think) you have to go to a different section to see the individual test failures. |
I am starting to believe that this is the case. The tests pass with the following patch: diff --git a/src/wallet/keys/hd_private.cpp b/src/wallet/keys/hd_private.cpp
index fa9d7dc9c..0b4201c74 100644
--- a/src/wallet/keys/hd_private.cpp
+++ b/src/wallet/keys/hd_private.cpp
@@ -266,7 +266,12 @@ hd_private hd_private::derive_private(uint32_t index) const NOEXCEPT
hd_public hd_private::derive_public(uint32_t index) const NOEXCEPT
{
- return derive_private(index).to_public();
+ const auto tmp = derive_private(index);
+ if (tmp) {
+ return tmp.to_public();
+ }
+
+ return {};
}
// Operators.
I am no C/C++, so the code is just something quickly stitched together. Wdyt? |
That makes sense, I'll give it a look in a bit. |
If my suggestion is indeed the problem, I wonder what a proper fix should be. Maybe verifying |
I think people generally ignore secp invalidity and guard against deriving past the limit (since it's a guaranteed failure). |
Do you also backport bug-fixes? I see the same thing in |
Sure, if they're simple. |
It's either: hd_public hd_private::derive_public(uint32_t index) const NOEXCEPT
{
const auto derived = derive_private(index);
return derived ? derived.to_public() : hd_public{};
} or: hd_public hd_private::to_public() const NOEXCEPT
{
if (!valid_)
return {};
...
} as you suggested. I prefer the latter because it explicitly guards both scenarios which also benefits the case where the caller has not guarded a private key before conversion (as opposed to derivation) to public. |
Feel free to make the change into both repos. |
In case `hd_private` is invalid, the `to_public()` derivation should fail. The `hd_private` might be invalid from various reasons, such as deserializing invalid key or derived, using `derive_private` into an invalid one. Finally, nitpick: `return {};` is enclosed in curly brackets to make the if statement scope explicit.
Thanks for the code examples. I also like the second one more. I've added a couple more tests to also:
|
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.
Could you please squash these to a single commit?
|
||
if (!valid_) { | ||
return {}; | ||
} |
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.
Avoid bracing for single line (also braces would be balanced).
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.
I like to be explicit about the if
scope, as I have seen a new code added (maybe under the influence of Python-like, whitespace aware languages), without adding the braces. Tests should hopefully catch that though.
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.
@@ -128,4 +128,112 @@ BOOST_AUTO_TEST_CASE(hd_private__derive_public__long_seed__expected) | |||
BOOST_REQUIRE_EQUAL(m0xH1yH2_pub.encoded(), "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"); | |||
} | |||
|
|||
BOOST_AUTO_TEST_CASE(hd_private__constructor__null_key_decodes_to_invalid__expected) |
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.
This:
hd_private__constructor__null_key_decodes_to_invalid__expected
would be better as:
hd_private__constructor__null_key__decodes_to_invalid
Others as well. Third part is the thing being tested and fourth part is the expectation.
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, will improve.
BOOST_REQUIRE_EQUAL(xpub_255.lineage().depth, 255); | ||
BOOST_REQUIRE(xpub_255); | ||
|
||
// depth overflows uint from 255 to 0 |
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.
I don't think it's actually overflowing here, it should be the default (invalid/null) key, and the depth value for that happens to be zero.
Moved to #1637 |
Hi, based on the discussion in #1623, I've decided to create a few test cases. I've manually created an extended private key with the depth set to
254
and then incremented it twice, checking that the first derivation passes and the second one fails (for various combinations ofhd_private
,hd_public
andderive_private
orderive_public
).I've split the tests into two commits due to, surprisingly, some variants failing. In particular, it seems to be possible to hit the overflow without and when deriving a public key of depth
256
from a private key of depth255
. Unfortunately, the tests are quite some copy-paste (I am happy to be transparent about this), but I could not see where the test (for the failing ones) would be incorrect.