Skip to content

Conversation

@jonasschnelli
Copy link
Contributor

This will add basic bip32 support for the current wallet. I testes the PR in serval environments, but, still needs more testing.
Feedback of any type is highly welcome.

Concept

  • At the moment, all HD operations are independent/separated from the existing normal single key operations (allows better testing and a smother transition to HD wallets).
  • The wallet is still capable of using and producing single keys.
  • The user can define his desired chainpath like m/44'/0'/0'/c or m/0'/c
    • "m" stands for master, "c" for switch between internal (1) and external (0) chain
  • HD private keys are not stored in the database. If they are required, they get derived via the stored master seed and the available metadata (child index, chain path).
  • When creating a new hd chain of keys (hdaddchain add <chainpath> <masterseed>), the rpc command will add the generated master seed as hex to the response (user can store it and use it as backup).
  • Bip39 is not supported (and I don't plan to support it). Instead of bip39 i'd like to use @maaku base-32 error correction encoding (https://gist.github.com/maaku/8996338). Users could write down the used master seed (or print if we would add a such option to the GUI). (not sure about that but i think users should create a print of the seeds 32byte hex representation. Brainwallets tend to have weak security and are social-conceptual a bad idea).
  • There is one new wallet state variable (activeHDChain) which is somehow unavoidable (change addresses, etc.).

Persistance

  • HD pub keys are stored within a new object CHDPubKey. Additional to the raw pub key there is also information about the chain, depth, etc.
  • HD master seeds are stored as a 32byte raw data blob (encrypted with the given masterkey if wallet is/gets encrypted)

RPC

This adds 5 new rpc commands

  1. hdaddchain allows to add a new hd chain of keys
  2. hdgetaddress get next available external key
  3. hdsetchain set the active chain of keys (enables basic chain rotation)
  4. hdsendtoaddress copy of sendtoaddress but uses a hd pub key for the change address
  5. hdgetinfo list the available chains of keys

GUI

The UI still uses normal key operation. Switch to HD would be trivial, but, i'd like to extend the "first start wizard" to allow selecting a chain and encrypt before writing unencrypted data to the database.

What's next

  • The rpc test is a little bit mininalistic and I have plans to extend the test
  • Support for pub key only wallets (basically this is trivial, it only needs to allow master pub key or extended int./ext. chain pub keys as input for hdaddchain RPC. Signing would fail in a such case.
  • Support for HDM (allow one or two additional master pub keys per chain of keys to allow creating of multisigaddresses with standard hdgetaddress call [as well as change output keys]).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be based on sendmany (not sendtoaddress) and take the chain to use for change as a parameter. The existing RPCs and GUI should use the hdsetchain-configured default - no need for random keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @luke-jr. The hd'ness should be used by default in future. But maybe to give a save transition, a first version would keep the hd functions separated. Users could decide where they want to use hd and where not. Maybe not everyone likes to use hd change keys for current non-hd inputs.

But right, this PR would slightly mix hd and non-hdness anyway. Unspents and balance would be shared.

@jonasschnelli jonasschnelli changed the title add HD/Bip32 support Add HD/Bip32 support Jun 10, 2015
@laanwj laanwj added the Wallet label Jun 12, 2015
@fanquake
Copy link
Member

One warning while compiling

  CXX      qt/qt_libbitcoinqt_a-walletmodeltransaction.o
qt/walletmodeltransaction.cpp:20:5: warning: delete called on 'CReserveKey' that has virtual functions but non-virtual destructor [-Wdelete-non-virtual-dtor]
    delete keyChange;
    ^
1 warning generated.

hdgetaddress doesn't seem to work?

tinyformat: Not enough conversion specifiers in format string (code -1)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/acctual/actual

@jonasschnelli
Copy link
Contributor Author

@fanquake: thanks for the tests. Will have a look at it. Before calling hdgetaddress did you call hdaddchain?

@rebroad
Copy link
Contributor

rebroad commented Jun 18, 2015

can this be used to import master seeds? If not, would this require much additional work?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not read HelpExampleCli("hdaddchain", "") ? Since by providing "set" as an argument, you must also provide a seed ... I believe the intention was to provide a example run without providing a seed, which should just run as hdaddchain without arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Will fix this.

@jonasschnelli
Copy link
Contributor Author

@rebroad: right. This PR would enabled HD features in the main bitcoin-core wallet. It won't affect the current ways of generating keys and sending coins. It's totally independent form the rest of the wallet features. So it is relatively save to use/test this.

You can import hd seeds, but only in raw hex. There is no support for Bip39 word lists. I'm pretty sure you'll find a tool to convert bip39 wordlists into 32byte master seed represented in hex.

@afk11
Copy link
Contributor

afk11 commented Jun 25, 2015

I haven't gotten to testing this yet, but why did you not decide to accept a base58 encoded key?

The first reason against accepting raw hex is there's no way to validate the key without visually comparing. And extended keys can often look similar at the start. xpub/xprv's include a checksum, so the user can be sure they entered the right thing.

The other reason is extensibility, since adding watch-only chains implies there won't be any master hex around to use.

@jonasschnelli
Copy link
Contributor Author

@afk11: The lack of base58check encoded master keys is only because it's not implemented yet (didn't find time to do it). But it's very trivial to add. I decided to export the master seed hex to potentially allow to create a bip39 mnemonic (there could be a tool hex-seed->bip39mnemonic). IIRC, you can't create a bip39 mnemonic from the master private key (not sure). Somehow i feel to store a hd chains of keys in the very root, the seed and not in the first derivation, the master private key.
Indeed, you can't visually verify a hex master seed. At the moment i'm not sure what's best practice here. I'd like to avoid Bip39 for security reasons and so im stuck now with hex encoding for the 256bit entropy.

But right. Especially for watch-only and HDM wallets, base58check encoded public key importing and usage as external chain root is extremely helpful. I also wrote that in the PR description (see "whats next").

@jonasschnelli jonasschnelli force-pushed the 2015/06/wallet_bip32 branch from 3555883 to 41f6c1c Compare July 23, 2015 10:44
@jonasschnelli
Copy link
Contributor Author

Rebased and added the base58check encoded master pub/priv key to the hdaddchain RPC output.

@jonasschnelli jonasschnelli force-pushed the 2015/06/wallet_bip32 branch from f7de7e2 to e902fa2 Compare July 23, 2015 19:55
@jonasschnelli
Copy link
Contributor Author

Added some commits on top to address nits and add a feature for creating a hd chain of keys with a existing base58check encoded extended master private key (xpriv...).
Supporting watch only wallets with a given base58check encoded pub key of the external key chain will follow.

@jonasschnelli
Copy link
Contributor Author

Most of the bip32 work is also included in CoreWallet (https://github.com/jonasschnelli/bitcoin/tree/2015/05/corewallet), which aims to be a replacement/twin for the current wallet with the option of completely split it of, of the bitcoin-core repository.

The PR is an option for quicker adding bip32 supporting to bitcoin-core (for the ones who can compile bitcoin-core by them self) or for the case where CoreWallet could fail to reach a stable level.

@jgarzik
Copy link
Contributor

jgarzik commented Jul 23, 2015

concept ACK

@jonasschnelli jonasschnelli force-pushed the 2015/06/wallet_bip32 branch 2 times, most recently from 99340d4 to a4ce300 Compare July 23, 2015 20:31
@afk11
Copy link
Contributor

afk11 commented Jul 27, 2015

@jonasschnelli oops, missed the notification on this.. You're right you can't go from hex to bip39 mnemonic, as mnemonics have pbkdf2 applied to them. +1 on base58 also.

I noticed you have a hard limit of 32 bytes of hex for the provided seed. This isn't correct as BIP32 doesn't specify a size, but rather states the binary seed may be short, or as long as 512 bits (128-256 recommended), since it's hashed with HMAC.

Small PR against your branch for this: jonasschnelli#8

Also, chalk it up to not reading the docs carefully, but I didn't expect hdaddchain to create a random key when I started testing. Maybe explicitly mention this will happen if no hex seed / master private key is provided?

@jonasschnelli
Copy link
Contributor Author

@afk11: Meh. Right. I see your point. So you are saying once you have created a bip32 chain with a entropy over GetRandBytes() you can't build a bip39 wordlist (sorry, not familiar with bip39)?

This would definitively mean using base58c for the seed output, or even better, just the master private key in base58c. Or do you see any reason to keep the seed?

Maybe it should also support importing a bip39 wordlist to create a bip32 chain.

@jonasschnelli jonasschnelli force-pushed the 2015/06/wallet_bip32 branch from a4ce300 to 7e4471a Compare July 27, 2015 17:56
@afk11
Copy link
Contributor

afk11 commented Jul 27, 2015

Yes, that is correct - the entropy used to produce the bip39 mnemonic is different to the seed that is produced.

I would suggest only displaying the base58 master key. It would keep the API consistent, since in certain situations the initial seed used to yield the master key won't be available, and in all other dumphd... cases it'll probably just be the xprv/xpub.

There isn't much reason to keep the hex seed - the same can be said for BIP39 mnemonics (and their passphrases) I guess. You could have separate RPC methods to create a new master via both methods.

I'm not sure if anything is lost in doing this. If someone dumps their master xprv, they can import it using hdaddchain just the same as if they had the mnemonic / hex.

@rnicoll
Copy link
Contributor

rnicoll commented Aug 6, 2015

Could you point me at some background on the concerns about BIP 39 and security please?

Seed interoperability with other wallets would seem like one of the major advantages to HD wallet support, although being about to import is IMHO the most important part of that use case, so reference client can be used to recover where another wallet fails.

@jonasschnelli
Copy link
Contributor Author

@rnicoll: Mostly because Bip39 can harm your security:

The 2048 kdf rounds might be okay for embedded USB hardware wallet. But IMO nothing that i'd like to have on my full-node wallet.

But i'm pretty sure you can export/convert a bip39 memonic into a master xpriv key (maybe on bip32.org)?
Also feel free to patch the RPC hdaddchain with import support for bip39.

@rubensayshi
Copy link
Contributor

something to keep in mind with BIP39 is that because seed = hash(mnemonic, pass) it doesn't allow for changing passwords :/

@dcousens
Copy link
Contributor

@rubensayshi, at least, not without transferring all the BTC.

@dcousens
Copy link
Contributor

dcousens commented Dec 2, 2015

Needs rebase

@jonasschnelli jonasschnelli force-pushed the 2015/06/wallet_bip32 branch 2 times, most recently from 5f8cfe6 to 3327d29 Compare December 23, 2015 09:59
@jonasschnelli
Copy link
Contributor Author

Rebased and updated.

  • Not the default keypath is m/c' = (m/0'/0' for first external key and m/1'/0' for first internal/change key).
  • Support for private child key derivation (default).

This PR is relatively complex and are meant for user who are seeking full control over their hd structure. I have plans to also open a smaller, simpler HD patch.

@jonasschnelli
Copy link
Contributor Author

Closing in favor of #8035.
Some parts of this PR could be used to extend HD functionality if and once #8035 has been merged.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.