Seeding a Bitcoin wallet in Elixir

How to build an HD wallet with extended keys in Elixir?

Since we already know how to produce a mnemonic phrase and then calculate a wallet seed, we can now learn a way to generate private keys and almost complete the process of building an offline Bitcoin wallet.

Deterministic or “seeded” wallets contain private keys that are all derived from a common seed, sometimes called a master key. All the keys in this type of wallet are related to each other and can be generated again if one has the original seed.

There are a number of different key derivation methods used in deterministic wallets. The most commonly used derivation method, defined by the BIP-32 standard, uses a tree-like structure and is known as a hierarchical deterministic or HD wallet. Keys derived in a tree structure can derive a sequence of children keys, each of which can derive a sequence of grandchildren keys, and so on, to an infinite depth.

The standard is described here:

Master private key and chain code

If you are implementing a bitcoin wallet, it should be built as an HD wallet, with a seed encoded as mnemonic code for backup, following the BIP-32, BIP-39, BIP-43, and BIP-44 standards.

The root seed is the input into the HMAC-SHA512 algorithm and the resulting hash is used to create a master private key and a master chain code.

Note that Bitcoin seed is the key to our function while seed is the data to be hashed. Then, we split the result into two 32 -byte sequences, treat the first one as master private key, and the second as master chain code.

The master private key then generates a corresponding master public key using the normal elliptic curve multiplication process we did in the first article:

According to the standard, we will define a function that derives a number of child keys from parent keys. In order to prevent these from depending solely on the key itself, we extend both private and public keys first with an extra 256 bits of entropy. This 32 -byte extension is called the chain code.

Extended keys

The term “extended key” could also be thought of as an “extensible key” because such key can be used to derive its children. Extended keys are stored and represented simply as the concatenation of the 256 -bit key and 256 -bit chain code into a 512 -bit sequence. There are two types of extended keys:

An extended private key is the combination of a private key and chain code and can be used to derive child private keys (and from them, child public keys).

An extended public key is a public key and chain code, which can be used to create child public keys

They are encoded using Base58Check to easily export and import between different BIP-32 –compatible wallets. The Base58Check coding for extended keys uses a special version number that results in the prefix xprv and xpub to make them easily recognizable.

Because the extended key is 512 or 513 bits, it is also much longer than other Base58Check -encoded strings we have seen previously.

The HD protocol uses different index numbers to indicate whether a normal or hardened key should be generated. Index numbers from 0x00 to 0x7fffffff will generate a normal key; index numbers from 0x80000000 to 0xffffffff will generate a hardened key :

An extended key is just a base58 -encoded serialization of a few pieces of data:

An xprv or xpub 4 -byte prefix to indicate the network it belongs to:

+------+---------+--------+

| | private | public |

+------+---------+--------+

| main | xprv | xpub |

+------+---------+--------+

| test | tprv | tpub |

+------+---------+--------+

A depth in the hierarchy is incremented between the parent and its child, so here it’s just 0 .

in the hierarchy is incremented between the parent and its child, so here it’s just . A parent’s fingerprint are the first 4 bytes of the hash160 of the public key of the parent. Hence, in case of a master key, it’s of course 0 .

are the first bytes of the of the public key of the parent. Hence, in case of a master key, it’s of course . A key index is the number of the key at the current hierarchy level. It’s always 4 bytes with zeros prepended if needed.

The entire key can be serialized as follows:

There’s a small change in case of public key serialization. Unlike the private key, which has 32 bytes, it has 33 of them instead.

In practice, public keys are encoded in the following ways:

0x02 + [ 32 -byte X coordinate] (if the Y coordinate is even)

+ [ -byte coordinate] (if the coordinate is even) 0x03 + [ 32 -byte X coordinate] (if the Y coordinate is odd)

The Y coordinate can be of course computed from the X one (if you know the sign). This trick is used in the recent version of several wallet client. It’s called compressed public keys, and it means that when spending a transaction output, the public key stored in the spending script (and thus the blockchain) only contains the X coordinate and a marker byte to denote which of both Y coordinates is used. This is slightly slower to validate, but saves space.

Eventually, we do Base58check encoding of the final result.

Subscribe to get the latest content immediately

https://tinyletter.com/KamilLelonek

Summary

In this article, we went through the process of generating master key and both public and private extended keys directly from seed.

We can now transfer them between different wallets and derive children keys from them.

If you are interested in the complete code, it’s available right here:

You will find there useful examples and the corresponding test suite.