Add ‘sethdseed’ RPC to initialize or replace HD seed. #11085

pull dooglus wants to merge 1 commits into bitcoin:master from dooglus:set_hd_seed changing 5 files +71 −0
  1. dooglus commented at 8:49 am on August 18, 2017: contributor

    As mentioned in #11070, I wanted to be able to use HD addresses in my legacy wallet.

    Is a change along these lines acceptable? What needs changing?

    I’m aware it’s missing tests. I’ll add those if there’s a chance this could be merged. If not, my wallet now uses HD addresses, so that’s good enough for me.

  2. in src/wallet/wallet.cpp:1416 in 8152462f58 outdated
    1409@@ -1410,7 +1410,11 @@ CPubKey CWallet::GenerateNewHDMasterKey()
    1410 {
    1411     CKey key;
    1412     key.MakeNewKey(true);
    1413+    return GenerateNewHDMasterKey(key);
    1414+}
    1415 
    1416+CPubKey CWallet::GenerateNewHDMasterKey(CKey key)
    


    jonasschnelli commented at 9:13 am on August 18, 2017:
    I think this should be called DeriveNewMasterHDKey()

    dooglus commented at 9:27 am on August 18, 2017:
    Good point.
  3. in src/wallet/rpcwallet.cpp:3184 in 8152462f58 outdated
    3179+        if (pwallet->HaveKey(key.GetPubKey().GetID()))
    3180+            throw JSONRPCError(RPC_WALLET_ERROR, "Key already exists in wallet");
    3181+
    3182+        masterPubKey = pwallet->GenerateNewHDMasterKey(key);
    3183+    } else
    3184+        masterPubKey = pwallet->GenerateNewHDMasterKey();
    


    jonasschnelli commented at 9:16 am on August 18, 2017:
    What does prevent from setting a new seed in an existing HD wallet? Or do we want to allow that? If so, we should at least warn in the help text or even add an additional boolean to make sure the user is aware of the implications (hopefully).

    dooglus commented at 9:25 am on August 18, 2017:
    It allows you to replace your existing HD seed with a new one, for example if you are worried that your old seed was exposed to a third party, you can switch to a new one without having to lose transaction history.

    jonasschnelli commented at 9:27 am on August 18, 2017:
    But shouldn’t that worry (compromised seed key or compromised sub-key) lead to moving the funds to a new seed? It may hurts privacy is done in a single N-to-1.

    dooglus commented at 5:03 pm on August 18, 2017:
    Yes, you would want to move funds from keys you suspect are compromised. The user is responsible for deciding how and when to move the funds. This pull request simply makes it possible to switch to a new uncompromised set of addresses.
  4. in src/wallet/rpcwallet.cpp:3243 in 8152462f58 outdated
    3185+
    3186+    if (!pwallet->SetHDMasterKey(masterPubKey))
    3187+        throw JSONRPCError(RPC_WALLET_ERROR, "Storing master key failed");
    3188+
    3189+    if (fFlushKeyPool)
    3190+        pwallet->NewKeyPool();
    


    jonasschnelli commented at 9:17 am on August 18, 2017:
    Not flushing the pool may lead to unrecoverable funds. If one sets an HD seed without flushing, backup directly, he may miss -keypoolsize keys (maybe 100, maybe 1000). I’d say a flush is mandatory.

    dooglus commented at 9:27 am on August 18, 2017:
    I don’t understand. How do old un-flushed keys in the mempool cause loss of funds if the HD seed is changed but not if it isn’t? They’re either backed up or they’re not.

    jonasschnelli commented at 9:31 am on August 18, 2017:
    Your right. It would only be a problem if the user considers only backing up the seed (user may thinks I just need to backup the 64 char hex string) at this point.

    dooglus commented at 5:06 pm on August 18, 2017:

    If he only backs up the seed, he loses all his funds in pre-HD addresses whether we flush the keypool or not.

    We default to flushing the keypool. If a user chooses not to flush the pool I think he likely understands what he’s doing.

  5. jonasschnelli changes_requested
  6. jonasschnelli commented at 9:19 am on August 18, 2017: contributor

    Not entirely sure if we should allow mixed wallets (IMO multiwallet would provide a more sane path to run HD and nonHD side a side).

    I could imagine that sethdseed is available and marked as expert feature. It’s one of the commands that has the possibility to footgun and may results in lost funds (==careful concept-thinking required)

  7. jonasschnelli added the label Wallet on Aug 18, 2017
  8. dooglus commented at 9:28 am on August 18, 2017: contributor

    IMO multiwallet would provide a more sane path to run HD and nonHD side a side

    I wasn’t aware of ‘multiwallet’ until I merged my changes to the master branch, and still haven’t looked into it in any detail. If it allows me to see my pre-HD and post-HD transactions in a single unified list then I agree.

  9. achow101 commented at 0:03 am on August 19, 2017: member
    This RPC should probably also bump the version number to at 139900 so that HD chain split is supported as well.
  10. TheBlueMatt commented at 1:17 am on August 21, 2017: member

    @jonasschnelli hmm, multiwallet has a rather significant usability burden (eg not being able to spend from both sides) vs just allowing people to add an HD master key to their wallet. I’m not sure why we wouldn’t allow people to upgrade their wallet using the same -upgradewallet path.

    Independantly, I do think we need the option to allow someone to rotate their HD master key if they desire (with appropriate backup warnings).

  11. in src/wallet/rpcwallet.cpp:3194 in 81c1d0cb25 outdated
    3141+    }
    3142+
    3143+    if (request.fHelp || request.params.size() > 2) {
    3144+        throw std::runtime_error(
    3145+            "sethdseed ( \"flushkeypool\" \"seed\" )\n"
    3146+            "\nSet or reset the HD wallet seed.\n"
    


    TheBlueMatt commented at 1:21 am on August 21, 2017:
    Needs way more documentation here (note it keeps old keys, you probably want to backup immediately after using this and before otherwise using your wallet, etc).
  12. dooglus commented at 8:19 pm on August 21, 2017: contributor

    @TheBlueMatt > I’m not sure why we wouldn’t allow people to upgrade their wallet using the same -upgradewallet path.

    I don’t think -upgradewallet should invalidate old backups, whereas setting the HD wallet seed does invalidate old backups if it flushes the keypool, which it does do by default. Perhaps that’s an argument for keeping the seed-setting code separate from -upgradewallet, especially since a user may want to change his HD seed after setting it.

    It would make sense to have -upgradewallet set an HD seed if it’s not already set, but not flush the keypool. In that way the old backup isn’t invalidated, and the user’s old strategy of “make new backup every 100 transactions” remains safe.

  13. TheBlueMatt commented at 8:52 pm on August 21, 2017: member

    @dooglus hmm, well if we dont want to use the same system (we could, of course, call the argument something different for this upgrade), then we’ve horribly fucked up (we used a wallet version number for HD support, so if we dont allow upgrade using the same version number stuff then we’re left without any good options to add any other future features, eg segwit support).

    I’m fine with, eg, refusing to upgrade past HD unless you use -upgradetohd, and also it seems perfectly reasonable to not flush keypool with the HD upgrade (why tie them together? The user can flush separately if they want).

    I do think we need the ability to rotate the HD key before we add any other features past HD (cause we shouldnt force users into HD unless they can still use it as before by rotating their HD seed), so thanks for this.

  14. in src/wallet/rpcwallet.cpp:3170 in 81c1d0cb25 outdated
    3165+    bool fFlushKeyPool = true;
    3166+    if (request.params.size() > 0)
    3167+        fFlushKeyPool = request.params[0].get_bool();
    3168+
    3169+    CPubKey masterPubKey;
    3170+    if (request.params.size() > 1) {
    


    promag commented at 9:44 pm on August 21, 2017:
    0if (!request.params[1].isNull()) {
    
  15. in src/wallet/rpcwallet.cpp:3166 in 81c1d0cb25 outdated
    3161+
    3162+    LOCK2(cs_main, pwallet->cs_wallet);
    3163+    EnsureWalletIsUnlocked(pwallet);
    3164+
    3165+    bool fFlushKeyPool = true;
    3166+    if (request.params.size() > 0)
    


    promag commented at 9:44 pm on August 21, 2017:
    0if (!request.params[0].isNull()) {
    
  16. in src/wallet/rpcwallet.cpp:3165 in 81c1d0cb25 outdated
    3160+    }
    3161+
    3162+    LOCK2(cs_main, pwallet->cs_wallet);
    3163+    EnsureWalletIsUnlocked(pwallet);
    3164+
    3165+    bool fFlushKeyPool = true;
    


    promag commented at 9:45 pm on August 21, 2017:
    0bool flush_key_pool = true;
    
  17. in src/wallet/rpcwallet.cpp:3173 in 81c1d0cb25 outdated
    3168+
    3169+    CPubKey masterPubKey;
    3170+    if (request.params.size() > 1) {
    3171+        CKey key;
    3172+        std::string strKey = AccountFromValue(request.params[1]);
    3173+        if (strKey.length() != 64 || !IsHex(strKey))
    


    promag commented at 9:45 pm on August 21, 2017:
    Missing { }.

    achow101 commented at 6:55 pm on September 6, 2017:
    Why not use a WIF key instead of a hex seed?

    dooglus commented at 4:58 pm on September 7, 2017:

    Good question. I wanted to import the HD seed from the wallet in which I had been testing HD derivation into my main wallet, and happened to have it in hex format.

    I’ll switch to requiring a WIF key.

  18. in src/wallet/rpcwallet.cpp:3176 in 81c1d0cb25 outdated
    3171+        CKey key;
    3172+        std::string strKey = AccountFromValue(request.params[1]);
    3173+        if (strKey.length() != 64 || !IsHex(strKey))
    3174+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Key should be 64 hex characters");
    3175+
    3176+        std::vector <unsigned char> vch = ParseHex(strKey);
    


    promag commented at 9:46 pm on August 21, 2017:
    Remove space before <.
  19. in src/wallet/rpcwallet.cpp:3179 in 81c1d0cb25 outdated
    3174+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Key should be 64 hex characters");
    3175+
    3176+        std::vector <unsigned char> vch = ParseHex(strKey);
    3177+        key.Set(vch.begin(), vch.end(), true);
    3178+
    3179+        if (pwallet->HaveKey(key.GetPubKey().GetID()))
    


    promag commented at 9:46 pm on August 21, 2017:
    Missing { }.
  20. in src/wallet/rpcwallet.cpp:3183 in 81c1d0cb25 outdated
    3178+
    3179+        if (pwallet->HaveKey(key.GetPubKey().GetID()))
    3180+            throw JSONRPCError(RPC_WALLET_ERROR, "Key already exists in wallet");
    3181+
    3182+        masterPubKey = pwallet->DeriveNewMasterHDKey(key);
    3183+    } else
    


    promag commented at 9:47 pm on August 21, 2017:
    Missing { }.
  21. in src/wallet/rpcwallet.cpp:3186 in 81c1d0cb25 outdated
    3181+
    3182+        masterPubKey = pwallet->DeriveNewMasterHDKey(key);
    3183+    } else
    3184+        masterPubKey = pwallet->GenerateNewHDMasterKey();
    3185+
    3186+    if (!pwallet->SetHDMasterKey(masterPubKey))
    


    promag commented at 9:47 pm on August 21, 2017:
    Missing { }.
  22. in src/wallet/rpcwallet.cpp:3189 in 81c1d0cb25 outdated
    3184+        masterPubKey = pwallet->GenerateNewHDMasterKey();
    3185+
    3186+    if (!pwallet->SetHDMasterKey(masterPubKey))
    3187+        throw JSONRPCError(RPC_WALLET_ERROR, "Storing master key failed");
    3188+
    3189+    if (fFlushKeyPool)
    


    promag commented at 9:48 pm on August 21, 2017:
    Missing { }.
  23. in src/wallet/wallet.cpp:1416 in 81c1d0cb25 outdated
    1409@@ -1410,7 +1410,11 @@ CPubKey CWallet::GenerateNewHDMasterKey()
    1410 {
    1411     CKey key;
    1412     key.MakeNewKey(true);
    1413+    return DeriveNewMasterHDKey(key);
    1414+}
    1415 
    1416+CPubKey CWallet::DeriveNewMasterHDKey(CKey key)
    


    promag commented at 9:49 pm on August 21, 2017:
    const CKey& key?
  24. in src/wallet/rpcwallet.cpp:3137 in 81c1d0cb25 outdated
    3131@@ -3132,6 +3132,66 @@ UniValue generate(const JSONRPCRequest& request)
    3132     return generateBlocks(coinbase_script, num_generate, max_tries, true);
    3133 }
    3134 
    3135+UniValue sethdseed(const JSONRPCRequest& request)
    3136+{
    3137+    CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    


    promag commented at 9:50 pm on August 21, 2017:
    Remove space before *.
  25. in src/wallet/rpcwallet.cpp:3169 in 81c1d0cb25 outdated
    3164+
    3165+    bool fFlushKeyPool = true;
    3166+    if (request.params.size() > 0)
    3167+        fFlushKeyPool = request.params[0].get_bool();
    3168+
    3169+    CPubKey masterPubKey;
    


    promag commented at 9:54 pm on August 21, 2017:
    master_pub_key?
  26. in src/wallet/rpcwallet.cpp:3180 in 81c1d0cb25 outdated
    3175+
    3176+        std::vector <unsigned char> vch = ParseHex(strKey);
    3177+        key.Set(vch.begin(), vch.end(), true);
    3178+
    3179+        if (pwallet->HaveKey(key.GetPubKey().GetID()))
    3180+            throw JSONRPCError(RPC_WALLET_ERROR, "Key already exists in wallet");
    


    promag commented at 9:59 pm on August 21, 2017:
  27. promag commented at 10:00 pm on August 21, 2017: member
    Missing tests for errors and success case.
  28. TheBlueMatt commented at 10:26 pm on September 5, 2017: member
    @dooglus any plans on addressing @promag’s comments? The ability to rotate the HD master key is a very, very nice change which I’d like to see much sooner rather than later to resolve some outstanding HD stuff by upgrading people to HD aggressively.
  29. dooglus commented at 3:52 pm on September 6, 2017: contributor

    @TheBlueMatt Sure, I’ll get right on that. Hopefully today.

    I wasn’t previously aware of the developer notes but now I am.

  30. promag commented at 4:12 pm on September 6, 2017: member
    Needs rebase too.
  31. in src/wallet/rpcwallet.cpp:3221 in 81c1d0cb25 outdated
    3167+        fFlushKeyPool = request.params[0].get_bool();
    3168+
    3169+    CPubKey masterPubKey;
    3170+    if (request.params.size() > 1) {
    3171+        CKey key;
    3172+        std::string strKey = AccountFromValue(request.params[1]);
    


    achow101 commented at 6:37 pm on September 6, 2017:
    Why are you doing AccountFromValue? There are no accounts here.

    dooglus commented at 4:48 pm on September 7, 2017:
    Fixed.
  32. achow101 commented at 6:59 pm on September 6, 2017: member

    Concept ACK. Comments inline.

    This also needs rebase, more documentation, and tests.

    I’m not sure if it is necessary or useful to be able to set your own seed. Or if it is necessary or useful to have the option to not regenerate the keypool from the seed.

  33. dooglus force-pushed on Sep 7, 2017
  34. dooglus commented at 4:44 pm on September 7, 2017: contributor

    Addressed @promag’s formatting nits. Removed trailing period from commit message. Rebased. Squashed.

    Didn’t add tests or backup warnings yet.

  35. dooglus force-pushed on Sep 7, 2017
  36. dooglus commented at 4:56 pm on September 7, 2017: contributor

    @achow101

    I’m not sure if it is necessary or useful to be able to set your own seed.

    I want to be able to make a backup of my wallet on paper, by writing down nothing but my seed.

    In order to restore from the paper backup I need to be able to set my own seed.

    For this use case we would also need a ‘dumpprivkey’ for the master seed.

    Or if it is necessary or useful to have the option to not regenerate the keypool from the seed.

    It’s possible the user makes regular weekly backups of his wallet. If we regenerate the keypool, he’s at risk of loss of funds until the next weekly backup is made. If we give him the option of not regenerating the keypool, his previous backup will remain valid until the next backup is made. It’s also possible he makes a backup immediately, but it is somehow corrupted. If we allow him to not regenerate his keypool then he has the possibility of recovering funds using older backups. If we insist he regenerates his keypool, we’re forcing him to rely entirely on the new backup he made, for no good reason.

  37. dooglus commented at 4:59 pm on September 7, 2017: contributor

    TODO:

    • add tests
    • add backup warnings
    • use WIF keys, not hex ones
  38. dooglus force-pushed on Sep 7, 2017
  39. dooglus commented at 8:54 pm on September 7, 2017: contributor

    Now the code accepts WIF keys, not hex, and I added a warning to the doc string saying that the user needs to make a new backup.

    Should I add tests to the existing test/functional/wallet-hd.py script?

  40. meshcollider commented at 9:06 am on September 9, 2017: contributor

    utACK https://github.com/bitcoin/bitcoin/commit/ddad8fc0b6e4046ac1ad752f917f55abbe64199a, awaiting tests

    ~This still wouldn’t offer support for the 512-bit seeds generated with BIP 39 though, maybe that could be considered~

    Travis error is unrelated and was fixed in #11271

    @dooglus Should I add tests to the existing test/functional/wallet-hd.py script?

    I think that would make the most sense since this is directly manipulating HD wallets, yep

  41. in src/wallet/rpcwallet.cpp:3198 in ddad8fc0b6 outdated
    3193+            "sethdseed ( \"flushkeypool\" \"seed\" )\n"
    3194+            "\nSet or reset the HD wallet seed.\n"
    3195+            "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
    3196+            + HelpRequiringPassphrase(pwallet) +
    3197+            "\nArguments:\n"
    3198+            "1. \"flushkeypool\"       (boolean, optional, default=true) Whether to flush old unused addresses from the keypool.\n"
    


    TheBlueMatt commented at 3:16 pm on September 11, 2017:
    Maybe clarify a bit further what this implies? (eg “ie the next address from getnewaddress will be from this new seed. If this is not set, the old seed’s derived addresses will be used until the keypool has been depleted.”).

    dooglus commented at 8:53 pm on September 12, 2017:

    So:

            "1. \"flushkeypool\"       (boolean, optional, default=true) Whether to flush old unused addresses from the keypool.\n"
            "                             If true, the next address from getnewaddress will be from this new seed.\n"
            "                             If false, addresses from the existing keypool will be used until it has been depleted.\n"
    

    Note that the keypool may not contain addresses derived from an old seed, and may contain pre-HD random addresses.

  42. in src/wallet/rpcwallet.cpp:3228 in ddad8fc0b6 outdated
    3223+        if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
    3224+
    3225+        // check that we don't already have this key, compressed or otherwise
    3226+        key2.Set(key.begin(), key.end(), !key.IsCompressed());
    3227+        if (pwallet->HaveKey(key.GetPubKey().GetID()) || pwallet->HaveKey(key2.GetPubKey().GetID())) {
    3228+            throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
    


    TheBlueMatt commented at 3:30 pm on September 11, 2017:
    nit: maybe “(either as an HD seed or as a loose private key)” to note that you cant use an existing loose private key as an HD seed.

    dooglus commented at 8:57 pm on September 12, 2017:
    In this comment, @promag said that there was a similar error elsewhere. I think he meant that I should use the same string so as not to unnecessarily increase the required translation effort. I’ll switch to using your string.
  43. TheBlueMatt commented at 4:40 pm on September 11, 2017: member
    In-line comments are mostly nits, but we need some thing to keep people from putting an HD key in their wallet without a walletupgrade here, probably making SetHDMasterKey fail unless wallet version supports HD. In the future we may have an upgrade function which this could use (with appropriate documentation).
  44. sipa commented at 10:42 pm on September 11, 2017: member
    @dooglus Can you rebase? There were some unrelated Travis that affected your PR, but have been fixed in master since.
  45. dooglus force-pushed on Sep 12, 2017
  46. dooglus commented at 2:48 am on September 12, 2017: contributor
    Rebased.
  47. dooglus commented at 8:49 pm on September 12, 2017: contributor

    @TheBlueMatt > we need some thing to keep people from putting an HD key in their wallet without a walletupgrade here, probably making SetHDMasterKey fail unless wallet version supports HD

    I’m not familiar with walletupgrade. I made my wallet in 2011 and never (to my knowledge) ran walletupgrade. I was able to use this PR to add an HD key to my 2011 wallet and it seems to work. What am I missing?

  48. Add 'sethdseed' RPC to initialize or replace HD seed 4a565cb8f8
  49. dooglus force-pushed on Sep 12, 2017
  50. TheBlueMatt commented at 3:23 pm on September 14, 2017: member
    @dooglus I think it may work, but a) it wont use hd-split (which it should, if possible) and b) old versions opening the wallet which has had HD key added to it will (I believe) open normally and add non-HD keys instead of failing to open, which doing a -walletupgrade will ensure.
  51. achow101 commented at 11:47 pm on January 22, 2018: member

    Needs rebase

    Wasn’t the plan to have this (or something similar) in for 0.16 since creating non-HD wallets is now a thing?

  52. achow101 commented at 2:10 am on February 27, 2018: member
    @dooglus Are you still working on this?
  53. dooglus commented at 7:27 am on February 27, 2018: contributor
    No I’m not. I don’t think I have what it takes to get this merged.
  54. achow101 commented at 5:46 pm on February 27, 2018: member
    I’ve picked this up in #12560
  55. dooglus commented at 6:08 pm on February 27, 2018: contributor
    Thank you.
  56. achow101 commented at 6:11 pm on March 14, 2018: member
    This should be closed as it is replaced by #12560
  57. dooglus commented at 7:56 pm on March 14, 2018: contributor
    OK.
  58. dooglus closed this on Mar 14, 2018

  59. laanwj referenced this in commit e03c0db08f on May 14, 2018
  60. DrahtBot locked this on Dec 16, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-05 19:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me