Hierarchical Deterministic Wallets: Ethereum

Belavadi Prahalad
Coinmonks
4 min readAug 1, 2018

--

image source

This article walks you through how to create ethereum compatible hierarchical deterministic wallets. This should enable you to generate your own public - private keys, checksums and addresses for transactions.

We’ll be using bip39, ethereumjs-tx, ethereumjs-util, hdkey and web3.js.

  • BIP39: To generate mnemonics
  • Ethereumjs-tx: To create the transaction (Set params and serialize)
  • Ethereum-util: To leverage checksum, private-public key creation and convert public key to address
  • HDkey: To construct Hierarchical Deterministic key trees

Also Read: Best Bitcoin Hardware Wallets

De Facto Rules:

  • Private keys authenticate your ownership of ether.
  • Losing your private keys implies that you no longer have access and cannot spend locked up ether. Consider it burnt.
  • Addresses are used for making and receiving transactions, not public keys.
  • Public keys and addresses aren’t synonymous.
  • Knowledge of either your public or private key will let you recreate your address.
  • Public and Private keys are used to establish digital signatures.
  • Generate a BIP 39 compatible mnemonic:
const mnemonic = bip39.generateMnemonic();

A mnemonic is a set of words which when reproduced in the exact sequence and order will recreate your private keys. Mnemonics evolved to help cryptocurrency hodlers remember or rather arrive at a way to retain a certain set of information in human memory which would enable them to recreate their private keys.

It is considerably easier to remember words in order like “cherry you receive shuffle ski wise youth roof shield private shaft shield” instead of “331d1e7724a2784fa4d75c96da9d68f23321e1cd0b55a4d2377192bdae2f10fc

  • Generate a master Public Key and a Private key:
const root = hdkey.fromMasterSeed(seed)const masterPrivateKey = root.privateKey.toString('hex');
const masterPubKey = root.publicKey.toString('hex');

An elaborate tree structure of public and private keys can be generated from one main public key pair.
In most of cryptography, operations are performed at byte level. It is converted to hex here to easily verify expected behaviour in human readable format.

A master public key pair is a key(pun unintended) portion of the HD tree.

Knowledge of the master key pair will allow the person to recreate the entire tree of keys. Consider the master key pair and master key mnemonic as high prime targets and guard with highest priority.

  • Generate a key pair from the HD tree
var path = "m/44'/60'/0'/0/0";const addrNode = root.derive(path)
const pubKey = ethUtil.privateToPublic(addrNode._privateKey)

The tree in itself can be generated with any* number of leaves and nodes. Ultimately, to derive a specific key pair, we’d need to specify the path to obtain its corresponding key pair.

Upon specifying a path, we derive a key pair from the corresponding root.
This process is deterministic.

There exist hardened keys and extended keys which form a part of the Hierarchical Deterministic tree structure. Hardened keys are inherently used at End point level and extended keys when there needs to be a sub tree generated.

You can read more about it here

  • Generate checksum and address
const addr = ethUtil.publicToAddress(pubKey).toString('hex');
const address = ethUtil.toChecksumAddress(addr)

From the derived public key, we compute the checksum to verify that it is indeed an ethereum address. I believe this is an addition as a result of EIP 55.

  • Constructing a transaction:
const params = {
nonce:0x6,
to: '0x4584159875418ef77D1142bEec0b6648BD8eDb2f',
value: '0x30000',
gasPrice: 0x09184e72a000,
gasLimit: 0x30000,
}

A transaction consists of parameters that state the nature of the transaction.

Nonce defines the order in which transactions from the address are processed. Two transactions in bearing the same nonce implies one gets rejected.
To: recipient address
Value: ether being transacted
Gas price and gas limit define the gas that can be consumed for this transaction

  • Sign a transaction
const tx = new ethTx(params);
tx.sign(addrNode._privateKey); // Signing the transaction
const serializedTx = tx.serialize()

A new constructor instance for the transaction is generated from the parameters specified. The transaction is hence signed with the private key from the address of a leaf node generated earlier in the HD tree.

The signed transaction is then serialized prior to broadcasting to a node.

Source code for this project can be downloaded here.
This is by no means production ready. This is merely a learning stint.

Credits: Prateek Reddy and BitCraft LLC

This article is a result of trying to understand the ethereum transactional process. Proposed edits, suggestions, corrections and rebuttals are encouraged.

Cheers!

--

--