Closed klim0v closed 3 years ago
@jcvernaleo @jakesyl
@klim0v thanks! Kind of swamped today but hope to get to this one by tomorrow.
@klim0v the decred code for this is identical to our original code (it was just copied from here of course). https://github.com/decred/dcrd/blob/master/hdkeychain/extendedkey.go#L320 Do we think decred's code needs this fix too? And if not, why?
@klim0v the decred code for this is identical to our original code (it was just copied from here of course). https://github.com/decred/dcrd/blob/master/hdkeychain/extendedkey.go#L320 Do we think decred's code needs this fix too? And if not, why?
Yes, I can help you do this.
@jcvernaleo Is everything okay here?
@klim0v sorry about that (and thanks for the reminder). Totally flake out on this. Could you just squash it and it will be good to go.
Then I think you should make a matching PR on decred too.
This is technically accurate, however, I will point out that fixing it means it will break anything that has derived keys using the existing code and therefore it is a breaking API change that should be accompanied with a major version bump. This change can and will suddenly cause people to be unable to derive the private keys they need to spend any coins which pay to public keys that were originally derived under the current code.
I'm really not sure what type of versioning the project is using nowadays, but this change could easily make coins unspendable for some consumers.
This is a decision for the maintainers of the project now, but I suspect this change should be rejected as is, despite it being technically correct, due to that. A likely reasonable solution would be to expose the existing derivation semantics via a legacy function and make sure to loudly warn about the difference.
Do we think decred's code needs this fix too? And if not, why?
This fix can't be deployed in Decred as is because of the aforementioned issue. The primary wallet software in widespread use derives keys based on the current code, so, any change in this regard would absolutely make people lose access to their coins which is obviously not acceptable.
Okay, definitely do not want to break things for existing usage.
@klim0v what was the initial motivation for this?
The motivation was certainly because the existing code does not conform with the BIP32 specification which results in different derivations in some circumstances than the spec calls for.
In particular the spec defines the function ser256(p): serializes the integer p as a 32-byte sequence, most significant byte first.
While it's not really explicit about it, that implies it might need to be padded with leading zeroes to ensure it is a full 32-bytes. The problem is that big.Int
in Go is arbitrary precision and consequently it strips the leading zero bytes in its serialization. The result is that values which end up with one or more leading zeroes have a different serialization than the spec calls for. In terms of the math operations this doesn't matter since e.g. 0x0123 = 0x123
, however, that serialization is fed through HMAC-512 which is sensitive to the exact serialization.
So, the fix is indeed technically accurate, as I stated above, however, changing it also has the aforementioned problem of breaking people who have secured coins with the the non-conformant derivation code.
The reason for this correction was the fact that our organization https://github.com/MinterTeam creates blockchain services, in which the creation of addresses is similar to the algorithm in ethereum.
When I wrote the SDK for golang using transaction signing, it turned out that in about 1% of cases, the addresses created from the seed phrases do not match the addresses received in the SDK written in other languages.
For this I used libraries such as:
which used your library to create the master key.
I agree with @davecgh that this fix, which is not part of the major version, could lead to the loss of wallets. But at the same time I would like to nevertheless restore the technical correctness of the algorithm, and at least indicate this inaccuracy in the description. Otherwise, it will lead to conflicts in other new projects.
Hmmm, okay, so I guess we have a few options here (and if I missed anything, someone please correct me):
Thoughts? Opinions? @Roasbeef @jakesyl ?
It looks like BIP32 was updated to add a 3rd test vector specifically for this case. Depending on how this proceeds, I would suggest adding that new 3rd test vector to TestBIP0032Vectors
accordingly.
It looks like BIP32 was updated to add a 3rd test vector specifically for this case. Depending on how this proceeds, I would suggest adding that new 3rd test vector to
TestBIP0032Vectors
accordingly.
Thanks for the link. I added my test to TestBIP0032Vectors. Do not close PR, please. I will contact bitcoin/bips and suggest that they add my test to the documentation.
A lot of packages use your bip32 implementation 😔
So does anyone have thoughts on the three sets of options I posted above?
As I said before, I think the right approach is to fix it, offer a legacy version which retains the old behavior (internally you just abstract the core logic into a function with a flag to strip the leading zeros on the child key or not and then call it with true/false from the respective exported function), ensure the major version is bumped, and loudly warn consumers about the change and make it clear that if they have created seeds and derivations using the existing code they must use the legacy version. However, for all new seeds, they need to use the new version.
Question for @Roasbeef
This code is used in all versions of LND as of current, correct?
I have added an optional parameter for this. We can make another wrapper for him. In the next major version, already do a wrapper with a check already for a false value
I think it can be added to the new release without changing the major version
This code is used in all versions of LND as of current, correct?
Yeah, btcwallet
uses it internally, so the default lnd
configuration does to derive all its keys. FWIW, with the way we made our seed format (aezeed
), it's possible for us to add a new "external" version, which would then have btcwallet
use this new (breaking) v2
API for those users moving forward. I agree with davec
that we need to change the major version (to make this explicit), then point new users as this new more compatible API.
Something like https://golang.org/pkg/testing/quick/#CheckEqual can likely be used to find any other divergences across the various BIP 32 implementations.
Of course the upside of not doing a new major version (which requires users to manually switch over as there would be a new sub-directory), is that if the API is broken, they'll be forced to make a decision when they upgrade, vs somehow hearing about the new more compatible API.
FWIW, with the way we made our seed format (aezeed), it's possible for us to add a new "external" version, which would then have btcwallet use this new (breaking) v2 API for those users moving forward.
Should be able to get away with only an internal version bump for aezeed since there's no modification to the serialization/interpretation of the internal payload.
Also @GuiltyMorishita might want to look at this too. This bug will affect Ginco wallet compatibility with other wallets.
@junderw Thank you for mentioning me. Ginco Wallet does not use the package, so there is no problem. I tried this test just in case and it passed.
@klim0v is there still anything useful in this PR, or can we close it ?
@Roasbeef/@klim0v what do we want to do here? :Looks like this pr needs to be updated if we're going to consider merging + @davecgh has pointed out some significant concerns
As far as I remember, this issue has been fixed in https://github.com/btcsuite/btcutil/pull/182. We now have the Derive
function, which is a replacement for the buggy Child
function (now removed). The old behaviour is still available via the DeriveNonStandard
function.
Correction of key length with initial zero The length of the child private key must be at least 32