Cardano 911 — Wallet Command Line Interface (Part Two).

Andy Jazz
Geek Culture
Published in
8 min readOct 12, 2021

This is a continuation of the first story written earlier. Unless you read the previous part, you should follow this link to read it:

Cardano 911 — Wallet Command Line Interface (Part One).

Byron-era vs Shelley-era addresses

We currently distinguish five different eras in Cardano, but I want to point out right away that two of them are fundamental to CLI — they are Byron and Shelley.

  • Byron era started on epoch 0
  • Shelley era started on epoch 208
  • Allegra era started on epoch 236
  • Mary era started on epoch 251
  • Alonzo era started on epoch 290

The key difference between Byron-type addresses (legacy) and Shelley-type ones is that Byron has a faster blockchain sync, whereas Shelley has a stake delegation and faster restoration of a wallet.

In addition, the aforementioned types of addresses are very different from each other visually. Here is how a Byron-era Base58-encoded addresses (deprecated now) generated in Daedalus might look like:

DdzFFzCqrhso9PfdXvRQ7hcaR1g6rDFc1VMXhthCpEuqioSh4bRKNizwvwUx6sdxSAUFmDr1v8nRkQJ7wojoyun68HsfMD7hLm7CfCKT

And here is a Shelley-era specimen encoded with Bech32 scheme:

addr1qyuudk3newmzjwcz4eyypnpcexvcyww4znuw5g40dlt7fdeecmdr8jak9yas9tjggrxr3jvesgua298cag723m7hujmspd3k22

WARNING: Both addresses are fictitious. Do not send funds there!

In the first part of this story, I showed how, using the command below, to display all unused addresses on the screen:

cardano-wallet address list \
--state unused 0aa0cb917989f070c1ce97ba0f78004e4e93231a

Let’s take a look at the twentieth address:

What are those 5 strings stored in array called derivation_path?

Derivation path

Knowing what a derivation path is and how it works is crucial. Derivation paths are related to HD Wallets, a type of wallets where you have a seed phrase that unlocks a number of accounts, addresses, or private keys across supported network.

A derivation path is a piece of data which tells a Hierarchical Deterministic (HD) wallet how to derive a specific key within a tree of keys. Derivation paths are used as a Bitcoin standard and were introduced with HD wallets as a part of BIP32. BIP32-Ed25519 is some sort of extension of BIP32 in Cardano.

A derivation path for up-to-date Cardano wallets looks this way:

m / purpose' / coin_type' / account_index' / role / address_index

There is nothing better than visualizing it.

Shelley-Era Derivation Path with 3 hardened indices

Well, let’s figure out what is what.

m

  • root master key.

purpose’

  • for Byron-era addresses, that were encoded with Base58 scheme, developers used value 44H, due to the fact that it identified BIP44-Ed25519.
m / 44H / 1815H / 0H / 0 / 19
  • for Shelley-era addresses that are encoded with Bech32 scheme, developers use new value 1852H, due to the fact that it identifies CIP1852. By the way, 1852 is the year when Ada Lovelace died.
m / 1852H / 1815H / 0H / 0 / 19

coin_type’

  • Each token that passed ICO has its own identifier called coin_type. ADA’s identifier is 1815, or 0x717 in hex. A full list of coin types you can find here. It is noteworthy that 1815 is the year when Ada Lovelace was born.

account_index’

  • typically 0, but can be any number from 0 to 2³¹. As you noticed, three values in the path are followed by H, which stands for hardened.

Although the cardano-wallet or cardano-address utilities will give you an output that looks OK, if you don't put H after each value, no payment address generated later on will be valid. If you send funds to such addresses they will be lost!

role

  • At the moment there are only three roles — “0” is an external chain, “1” is an internal chain, and “2” is a staking address. In Daedalus, you may have noticed the following derivation path for a wallet’s staking address.
m / 1852H / 1815H / 0H / 2 / 0
  • In the future, the role slot might be extended with new roles.

address_index

  • By default, Daedalus wallet v4.3 creates 20 addresses for each account. If you look at addresses’ indices, you’ll see that they are coming from 0 to 19.

When working with CLI, nothing prevents you from creating an address straight to some crazy index such as 1 million (using a flag address-pool-gap 1000000), but if you send funds to a payment address generated from that key, it won’t show up in your wallet as by default it expects one transaction every 20 addresses in sequence.

Root master keys

Before proceeding, I propose to create one more folder called /seeds inside cardano directory and add two new environment variables.

I’ve declared two new vars inside ~/.zshrc file.

export SEEDS="/Users/swift/cardano/seeds$SEEDS"
export KEYS="/Users/swift/cardano/keys$KEYS"

It’s a good idea to restart a Terminal app and, after restarting it, generate a 24-word seed:

cardano-wallet recovery-phrase generate > $SEEDS/primary

I went to ~/cardano/seeds/ directory and copied the sentence. Here it is:

cargo better circle waste aspect laptop title student announce forget version dolphin seminar census next gun lumber balcony maximum express trend first chronic grit

Time to spawn a 800-bit root private key for Shelley-era addresses. I have to mention that legacy styles are still supported in Cardano CLI, so can create not only Shelley-style wallets but also a deprecated wallets in Byron, Icarus and Jormungandr styles.

cat $SEEDS/primary \
| cardano-wallet key from-recovery-phrase Shelley > $KEYS/root.prv

The cat command (short for “concatenate“) is one of the most frequently used command in Linux/Unix/Mac operating systems. cat command allows us to create single or multiple files, view contain of file, concatenate files and redirect output in terminal or files.

and

The vertical pipe | is used to pipe one command into another. That is, it directs the output from the first command into the input for the second command.

Our first key shines brightly…

root_xsk1zpf2nxkrjlk94h2e3vkn5lyrujymnklhh9mkf3ehkhhu6x55p3whw2j9gpaspxh350cmct65frf5ggrjxtflf6w47lnr3u2dys9t00w47h7dtxu9k23mp3pc6lmsuns233y04mt7a240rtk67nnkr9n9eq09zkvc

Now we should execute a command for a corresponding root public key:

cat $KEYS/root.prv \
| cardano-address key public --with-chain-code > $KEYS/root.pub

Got nice public key with the “root_xvk1 prefix.

root_xvk19kvyaegkd0t6gem0xxvy8fvwqxased2h00zg5f2wpmfzzxcnl2gata0u6kdctv4rkrzr34lhpe8q4rzgltkha6427xhd4a88vxtxtjquymcr8

You may ask: really 800-bit private key? That’s true, let us count. The length of a root private key is 160 characters + “root_xsk1 prefix. A key is encoded with the Bech32 scheme. The Bech32 alphabet totally contains 32 characters — lowercase a to z letters and 0 to 9 numbers, but excluding the number 1 and the letters b, i and o to avoid reader confusion. Therefore, math is quite simple — you have 32¹⁶⁰ or 2⁸⁰⁰ possible combinations.

160 characters excluding “root_xsk1” prefix

Deriving account keys

When deriving child keys from a parent root key, you must provide either two, three or five path segments. Providing just first 3 or all 5 path segments (i.e. purpose, coin_type, account_index, role, address_index), you’ll be dealing with Shelley key. However, providing 2 path segments (i.e. account_index and address_index), you’ll be intending to derive a legacy Byron key.

With this in our heads, nothing can stop us from generating account keys.

cat $KEYS/root.prv \
| cardano-wallet key child 1852H/1815H/0H \
| tee $KEYS/account.prv \
| cardano-wallet key public --with-chain-code > $KEYS/account.pub

The tee command reads standard input, then writes its content to standard output. It’s normally used to split the output of a program so that it can be both displayed and saved in a file. The command can be used to capture intermediate output before the data is altered by another command or program.

Our first pretendent is an account private key:

acct_xsk1wzjkw933vws6kyf8he6dng5w67mmtreg5vu0ms6cx5p9g2y5p3w3ptwh5gxh8cjyu6m299cvmhr992589vwg53d0vpvn23f5rwmeqst2ycrkacr3peaskss8cw0wr49daamrxvqqw3860grjvkjnmzk25u69sr5s

and a second one is an account public key:

acct_xvk1eq6sddwe8z6wacc02prws0lznl62q8lgwnlxw7n5sdewar30tesx5fs8dms8zrnmpdpq0su7u822mmmkxvcqqaz057s8yed98k9v4fc5c9nj4

Address keys

Ready to move further? Let’s generate address keys providing all 5 path segments.

cat $KEYS/root.prv \
| cardano-wallet key child 1852H/1815H/0H/0/0 \
| tee $KEYS/address.prv \
| cardano-wallet key public --with-chain-code > $KEYS/address.pub

Our private address key:

addr_xsk1czawxjvtgwtd02rdqz0u429mdyv2l7ewqc0jn5ypupuux255p3wl5gnknmfwth923rtzmdy0mnhntd2mf6wjdujvu9nzlfnqr2n95y6pl9w3fpyxyl0pj26wamsqmn282lt35scwtf7fkctlaz4ard2z0qajq93m

And, of course, public address key:

addr_xvk16cu8v3duckqq3ac48ghc0h5har6dwln7lly4n5qsy4ydqa2fxrlyr72azjzgvf77ry45amhqphx5w47hrfpsuknundshl69t6x65y7qq8rand

As you know a payment address is formed from a public key:

cat $KEYS/address.pub | cardano-address address payment \
--network-tag 1 > $KEYS/address.pay

Do you wanna see it on a screen?

cat $KEYS/address.pay

You can see that the address is in short form, this is because it’s a payment part of Shelley address. Let’s read the excerpt from official documentation:

Payment part

Fundamentally, the first part of a Shelley address indicates the ownership of the funds associated with the address. We call it, the payment part. Whoever owns the payment parts owns any funds at the address. As a matter of fact, in order to spend from an address, one must provide a witness attesting that the address can be spent. In the case of a PubKeyHash, it means providing a signature of the transaction body made with the signing key corresponding to the hashed public key (as well as the public key itself for verification). For monetary scripts, it means being able to provide the source script and meet the necessary conditions to validate the script.

Delegation part

The second part of a Shelley address indicates the owner of the stake rights associated with the address. We call it, the delegation part. Whoever owns the delegation parts owns the stake rights of any funds associated with the address. In most scenarios, the payment part and the delegation part are owned by the same party. Yet it is possible to construct addresses where both parts are owned and managed by separate entities. We call such addresses mangled addresses or hybrid addresses.

Staking keys

I’m sure you guessed how to generate staking keys — just insert “2” value instead of “0” for role segment.

cat $KEYS/root.prv \
| cardano-wallet key child 1852H/1815H/0H/2/0 \
| tee $KEYS/staking.prv \
| cardano-wallet key public --with-chain-code > $KEYS/staking.pub

Their prefixes are stake_xsk1… :

stake_xsk1nrttj2wevrr4n8sm3gxpfx5v963y7mfdvmrt6vy247xm6tv5p3wn5nt2k89na2hpnc9f9s7drp87c84dcp0n7c36nyj2e3tlt68tamn5enexxa752rgyuyle9yg39qrasvhvzm2xa74xx4u5sscegzy4gcdue3ju

…and stake_xvk1, respectively:

stake_xvk180frhh998pkd6fhxfqnz5dpcftauzq7fqvczurlsxnaua0u5hq7hfn8jvdmag5xsfcflj2g3z2q8mqewc9k5dma2vdtefpp3jsyf23s6g6xle

Creating a delegation address

You have 5 sub-commands at your disposal allowing you to create the desired type of Cardano address:

  • bootstrap
  • payment
  • stake
  • delegation
  • pointer

We’ll borrow the delegation sub-command. This is how it looks like:

$ cardano-address recovery-phrase generate --size 24 \
| cardano-address key from-recovery-phrase Shelley > root.prv

$ cat root.prv \
| cardano-address key child 1852H/1815H/0H/2/0 > stake.prv

$ cat root.prv \
| cardano-address key child 1852H/1815H/0H/0/0 > address.prv

$ cat address.prv \
| cardano-address key public --with-chain-code \
| cardano-address address payment --network-tag mainnet \
| cardano-address address delegation $(cat stake.prv | cardano-address key public --with-chain-code)

That’s all for now. Read official documentation if you want to find out how to commit transactions.

If this post is useful for you, please press the Clap button and hold it.

Happy staking!

--

--