-
Notifications
You must be signed in to change notification settings - Fork 36.5k
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
[Wallet] Add simplest BIP32/deterministic key generation implementation #8035
Conversation
Nice minimalistic PR. One question though. Why to use m/0'/0'/i instead of something more standard? BIP32 proposes m/0'/0/i for external chains (I suppose Core in current implementation does not need internal/change chain). Or even better - use m/0/i, which is forward compatible with BIP44 (maybe Core will make it somedays). m/0/i is subset of BIP44 - an account. |
@@ -3036,6 +3104,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) | |||
strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); | |||
strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); | |||
strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); | |||
strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after bip32. Has only affect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); |
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 need for strprintf
. See previous line.
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.
Hmm... I have looked at walletbroadcast
, there strprintf
is also used. Isn't more flexible for the translation then?
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 think you want to strprintf(_(string))
the whole string and not two separate stings, as other languages might have different grammar. but consider it a nit.
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.
s/Has only affect/Only has effect/
Concept ACK Nice, small, elegant 👍 |
What should RPC |
Some import path needed then. |
Concept ACK |
@slush0: One reason for the current keypath is the use hardened-only key derivation. But I agree. We could discuss the default keypath and the keypath flexibility in a such follow up PR. This PR only intent to solve the "backup problem" by using deterministic key derivation. So we could even think of using m/k'. |
ce6332e
to
c3b8aa5
Compare
Fixed @paveljanik nits. |
@jonasschnelli It's actually quite useful feature, and it may be even for Core. I can imagine somebody who'll have Qt wallet awfully out of sync and will need to get his funds ASAP. Then loading the backup to some other software like Multibit HD will do the job. BIP44 is actually being used by most of current wallets so heading towards the same structure makes sense.
Fair point and I appreciate somebody is finally addressing it. |
LOCK(cs_wallet); | ||
|
||
// store the key as normal "key"/"ckey" object | ||
// in the the database |
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 the
c3b8aa5
to
30815d2
Compare
Force push fixed @paveljanik nits. Formatted some of the code with clang-format. |
accountKey.Derive(externalChainChildKey, 0 | 0x80000000); | ||
|
||
// derive child key at next index | ||
externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | 0x80000000); |
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 think you should check whether the derived key already exists in the keystore, and if so, try the next chaincounter. Otheriwse you'll give out the same keys twice after a backup restore.
EDIT: nevermind, that's not going to work. we'll also need to make keypool entries be regenerated when one is encountered in the blockchain or mempool. I think we can defer that...
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 added a loop to derive the next key as long as its not known by the wallet. I think this is required otherwise we might run into an exception if someone restores a backup or has imported a derived key of a higher child index.
Concept ACK So as I understand this, it won't affect the keys already in the wallet, right? It just generates all new keys using the HD master key? Also there should be an RPC command to dump the master private key. |
@achow101:
I agree. Have a look at #6265 and #7273. |
0141792
to
cc1df8f
Compare
@@ -574,6 +577,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface | |||
|
|||
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>); | |||
|
|||
/* the hd chain data model (internal and external counters) */ |
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.
external only for now? (and in the other 2 places in the PR)
@@ -3236,6 +3236,14 @@ bool CWallet::InitLoadWallet() | |||
|
|||
walletInstance->SetBestChain(chainActive.GetLocator()); | |||
} | |||
else |
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.
Can be simplified a bit:
else if (mapArgs.count("-usehd")) {
bool useHd = GetBoolArg("-usehd", DEFAULT_USEHD);
if (!walletInstance->hdChain.masterKeyID.IsNull() && !useHD)
return InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile));
if (walletInstance->hdChain.masterKeyID.IsNull() && useHD)
return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile));
}
(also please introduce a DEFAULT_USEHD constant)
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. Much simpler.
Force push updated last commit.
71ff55f
to
afcd77e
Compare
utACK afcd77e Even though this wallet change is reasonably risky, I think this has received enough review for correctness, and it should be merged now to make sure it gets more actual testing. |
… implementation afcd77e Detect -usehd mismatches when wallet.dat already exists (Jonas Schnelli) 17c0131 [Docs] Add release notes and bip update for Bip32/HD wallets (Jonas Schnelli) c022e5b [Wallet] use constant for bip32 hardened key limit (Jonas Schnelli) f190251 [Wallet] Add simplest BIP32/deterministic key generation implementation (Jonas Schnelli)
Post merge utACK. Will also test. |
Just found out about this PR. Would like to clarify the keypath: It's purpose number is I'm asking because otherwise it could be incompatible to how bitcoinj derives addresses. |
@schildbach: We have decided to go with a "hardened only derivation" version for the first implementation. Using On top of that, I personally think importing a xpriv into another wallet in order to "reconstruct everything" is not a very good concept. |
@jonasschnelli Yes that's fine. I just wanted to make sure there is no "partial overlap" with the bitcoinj impl. |
So, how does one import a BIP32 master key into an existing wallet? |
I'm switching to Core (from Electrum) after this lands in 0.13, thanks for your efforts. |
That functionality currently doesn't exist. Please read the OP: this is the smallest possible change to introduce BIP32 into Bitcoin Core. This made it realistic to get it reviewed and merged in a small timespan. More functionality can be added later. |
// key metadata is not required | ||
CPubKey pubkey = key.GetPubKey(); | ||
if (!AddKeyPubKey(key, pubkey)) | ||
throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); |
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.
Nit: Wrong function name
Post-merge review comments, sorry :/ (continued from IRC):
I think all but the first could easily be fixed even now, if we decide they are, indeed, issues. |
Thanks for the post-merge review Matt.
Yes. Agree. This was done after two of my former PR did not made it into the master (#6265 #7273) and I was looking for a simpler solutions.
This is to ensure we don't re-generate an already existing key (there are some cases where this would be possible otherwise). |
re: GenerateNewKey(): Not sure what your response was about (seems to be communication failure there), but it was pointed out to me that I missed the save line and was incorrect. |
I foget my sceond wallet passwor. Thats why i cant get backup. I need help |
@zihad944 what problem do you have specifically, what do you mean by "my second wallet password"? |
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.
src/wallet/walletdb.h
Probably most simplest HD implementation.
--usehd=0
CHDChain
) which contains the CKeyID for the master key together with the child key counterI'll add some tests once there are some conceptual acks (and also to keep the diff at a very minimum).