HD Key Generation · bitcoin-s

In modern Bitcoin wallets, users only need to write down
a sequence of words, and that sequence is a complete backup
of their wallet. This is thanks to what’s called Hierarchical
Deterministic key generation. In short, every wallet using HD
key generation has a root seed for each wallet, and this
seed can be used to generate an arbitrary amount of later
private and public keys. This is done in a standardized manner,
so different wallets can operate with the same standard.

If you want to jump into the details of how this work,
you should check out
BIP 32.

Bitcoin-S supports generating keys in this fashion. Here’s a
full example of how to obtain a wallet seed, and then
use that to generate further private and public keys:

import

scodec.bits._

import

org.bitcoins.core.crypto._

import

org.bitcoins.core.hd._

val

entropy:

BitVector

=

MnemonicCode

.getEntropy256Bits

val

mnemonicCode =

MnemonicCode

.fromEntropy(entropy) mnemonicCode.words

val

bip39Seed =

BIP39Seed

.fromMnemonic(mnemonicCode, password =

"secret password"

)

val

xpriv =

ExtPrivateKey

.fromBIP39Seed(

ExtKeyVersion

.

SegWitMainNetPriv

, bip39Seed)

val

xpub = xpriv.extPublicKey

val

segwitPath =

SegWitHDPath

.fromString(

"m/84'/0'/0'/0/0"

)

val

otherSegwitPath =

SegWitHDPath

(

HDCoinType

.

Bitcoin

, accountIndex =

0

,

HDChainType

.

External

, addressIndex =

0

) segwitPath == otherSegwitPath

Generating new addresses without having access to the private key

One the coolest features of HD wallets is that it’s possible
to generate addresses offline, without having access to the
private keys. This feature is commonly called watch-only
wallets, where a wallet can import information about all
your past and future transactions, without being able to
spend or steal any of your money.

Let’s see an example of this:

import

scala.util.

Success

import

org.bitcoins.core.protocol.script._

import

org.bitcoins.core.protocol.

Bech32Address

import

org.bitcoins.core.config.

TestNet3

val

accountPath =

BIP32Path

.fromString(

"m/84'/0'/0'"

)

val

accountXpub = {

val

accountXpriv = xpriv.deriveChildPrivKey(accountPath) accountXpriv.extPublicKey }

val

firstAddressPath =

SegWitHDPath

.fromString(

"m/84'/0'/0'/0/0"

)

val

firstAccountAddress = {

val

Some

(pathDiff) = accountPath.diff(firstAddressPath)

val

Success

(extPubKey) = accountXpub.deriveChildPubKey(pathDiff)

val

pubkey = extPubKey.key

val

scriptPubKey =

P2WPKHWitnessSPKV0

(pubkey)

Bech32Address

(scriptPubKey,

TestNet3

) } firstAccountAddress.value

val

nextAddressPath:

SegWitHDPath

= firstAddressPath.next

Signing things with HD keys

Please see sign.md for information on how to sign things with HD keys.