Skip to content

Instantly share code, notes, and snippets.

@pintaric
Created February 18, 2021 22:36
Show Gist options
  • Save pintaric/9321c6f16e0e54ce6099aebd05f0674c to your computer and use it in GitHub Desktop.
Save pintaric/9321c6f16e0e54ce6099aebd05f0674c to your computer and use it in GitHub Desktop.
Shell script for deriving key pairs/addresses from a Cardano wallet recovery phrase
#!/bin/bash
# -------------------------------------------------------------------------------------------------
# Author: Thomas Pintaric <[email protected]>
# Version: 1.0.0
# SPDX-License-Identifier: 0BSD
# -------------------------------------------------------------------------------------------------
# USAGE: ./derive_cardano_wallet_keys.sh [flags] args
# flags:
# -r,--recovery_phrase: text file containing your wallet's recovery phrase (default: 'recovery-phrase.txt')
# -n,--[no]new_mnemonic: generate new 24-word mnemonic (default: false)
# -y,--[no]assume_yes: assume yes as answer to all prompts and run non-interactively (default: false)
# -t,--[no]testnet: use testnet as network tag, otherwise use mainnet (default: false)
# -o,--output_dir: output directory (default: '.')
# -v,--[no]verbose: verbose output (default: false)
# -h,--help: show this help (default: false)
#
# EXAMPLE:
# > create_keys.sh -n -y -o . -v
# -------------------------------------------------------------------------------------------------
# Tested with:
#
# cardano-cli 1.25.1 - linux-x86_64 - ghc-8.6 git rev 9a7331cce5e8bc0ea9c6bfa1c28773f4c5a7000f
# cardano-wallet 2021.2.12 (git revision: d98f7084b092c74a3f2310ec4c9009b42d564f1f)
# cardano-address 3.2.0 @ 115174cc451d3fc6b90ce61782f841e51f271c3d
# -------------------------------------------------------------------------------------------------
if [ -n "${ZSH_VERSION:-}" ]; then
DIR="${(%):-%N}"
if [ $options[posixargzero] != "on" ]; then
setopt posixargzero
NAME=$(basename -- "$0")
unsetopt posixargzero
else
NAME=$(basename -- "$0")
fi
else
DIR="${BASH_SOURCE[0]}"
NAME=$(basename -- "$0")
fi
SCRIPT_DIR=$( builtin cd "$( dirname "${DIR}" )" > /dev/null && pwd ${PWD_OPT})
DEFAULT_RECOVERY_PHRASE_FILE=${SCRIPT_DIR}/recovery-phrase.txt
DEFAULT_MNEMONIC_WORD_COUNT=24
DEFAULT_OUTPUT_DIR=${SCRIPT_DIR}
if [ ! -f "${SCRIPT_DIR}/shflags" ]; then
wget -nc -q -O ${SCRIPT_DIR}/shflags https://raw.githubusercontent.com/kward/shflags/master/shflags
fi
# source shflags
. ${SCRIPT_DIR}/shflags
# define command-line arguments
DEFINE_string recovery_phrase ${DEFAULT_RECOVERY_PHRASE_FILE} "text file containing your wallet's recovery phrase" r
DEFINE_boolean new_mnemonic 'false' 'generate new ${DEFAULT_MNEMONIC_WORD_COUNT}-word mnemonic' n
DEFINE_boolean assume_yes 'false' 'assume "yes" as answer to all prompts and run non-interactively' y
DEFINE_boolean testnet 'false' 'use "testnet" as network tag, otherwise use "mainnet"' t
DEFINE_string output_dir ${DEFAULT_OUTPUT_DIR} "output directory" o
DEFINE_boolean verbose 'false' 'verbose output' v
# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
export OUTPUT_DIR=${FLAGS_output_dir}
if [ ! -d "${OUTPUT_DIR}" ]; then
echo "ERROR: Output directory does not exist."
echo "Expected location: "${OUTPUT_DIR}
[[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1
fi
export RECOVERY_PHRASE_FILE=${FLAGS_recovery_phrase}
if [ ${FLAGS_new_mnemonic} = ${FLAGS_TRUE} ] ; then
if [ -f "${RECOVERY_PHRASE_FILE}" ] && [ ${FLAGS_assume_yes} = ${FLAGS_FALSE} ]; then
echo "WARNING: The text file containing your wallet's recovery phrase (${RECOVERY_PHRASE_FILE}) will be overwritten."
read -p "Are you sure? " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
[[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1
fi
fi
# Generate new recovery phrase
cardano-address recovery-phrase generate --size ${DEFAULT_MNEMONIC_WORD_COUNT} > ${RECOVERY_PHRASE_FILE}
elif [ ! -f "${RECOVERY_PHRASE_FILE}" ]; then
echo "ERROR: Could not locate the text file containing your wallet's recovery phrase."
echo "Expected location: "${RECOVERY_PHRASE_FILE}
[[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1
fi
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo $(cardano-cli --version)
echo "cardano-wallet "$(cardano-wallet version)
echo "cardano-address "$(cardano-address version)
echo
fi
MNEMONIC_COLOR='\033[1;35m'
ROOT_KEY_COLOR='\033[1;31m'
PAYMENT_KEY_COLOR='\033[1;32m'
STAKE_KEY_COLOR='\033[1;34m'
ADDRESS_COLOR='\033[1;36m'
NO_COLOR='\033[0m'
# Convert the recovery phrase to an extended root private key
cardano-address key from-recovery-phrase Shelley < ${RECOVERY_PHRASE_FILE} > ${OUTPUT_DIR}/root.xsk
MNEMONIC_WORD_COUNT=$(cat ${RECOVERY_PHRASE_FILE} | wc -w)
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo "Recovery phrase (${MNEMONIC_WORD_COUNT}-word mnemonic): ${RECOVERY_PHRASE_FILE}"
echo -e ${MNEMONIC_COLOR}"$(cat ${RECOVERY_PHRASE_FILE})${NO_COLOR}"
echo
fi
# Get the public counterpart of the extended root private key
cat ${OUTPUT_DIR}/root.xsk | cardano-wallet key public --with-chain-code > ${OUTPUT_DIR}/root.xvk
export KEY_INDEX=0
if [ ${FLAGS_new_mnemonic} = ${FLAGS_TRUE} ] ; then
export NETWORK_TAG=testnet
else
export NETWORK_TAG=mainnet
fi
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo "Extended root signing key: ${OUTPUT_DIR}/root.xsk"
echo -e ${ROOT_KEY_COLOR}"$(cat ${OUTPUT_DIR}/root.xsk)${NO_COLOR}"
echo
echo "Extended root verification key: ${OUTPUT_DIR}/root.xvk"
echo -e ${ROOT_KEY_COLOR}"$(cat ${OUTPUT_DIR}/root.xsk)${NO_COLOR}"
echo
fi
# ===========================================================================================================
# Derive an extended payment verification/signing key pair from the extended root private key
# (The last segment in the path is the key index and can be incremented up to 2^31-1 to derive more keys.)
cat ${OUTPUT_DIR}/root.xsk | cardano-address key child 1852H/1815H/0H/0/${KEY_INDEX} > ${OUTPUT_DIR}/payment.xsk
cat ${OUTPUT_DIR}/payment.xsk | cardano-address key public --with-chain-code > ${OUTPUT_DIR}/payment.xvk
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo "Extended payment signing key: ${OUTPUT_DIR}/payment.xsk"
echo -e ${PAYMENT_KEY_COLOR}"$(cat ${OUTPUT_DIR}/payment.xsk)${NO_COLOR}"
echo
echo "Extended payment verification key: ${OUTPUT_DIR}/payment.xvk"
echo -e ${PAYMENT_KEY_COLOR}"$(cat ${OUTPUT_DIR}/payment.xsk)${NO_COLOR}"
echo
fi
# Derive an extended stake verification/signing key pair from the extended root private key
# (The last segment in the path is the key index and can be incremented up to 2^31-1 to derive more keys.)
cat ${OUTPUT_DIR}/root.xsk | cardano-address key child 1852H/1815H/0H/2/${KEY_INDEX} > ${OUTPUT_DIR}/stake.xsk
cat ${OUTPUT_DIR}/stake.xsk | cardano-address key public --with-chain-code > ${OUTPUT_DIR}/stake.xvk
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo "Extended stake signing key: ${OUTPUT_DIR}/stake.xsk"
echo -e ${STAKE_KEY_COLOR}"$(cat ${OUTPUT_DIR}/stake.xsk)${NO_COLOR}"
echo
echo "Extended stake verification key: ${OUTPUT_DIR}/stake.xvk"
echo -e ${STAKE_KEY_COLOR}"$(cat ${OUTPUT_DIR}/stake.xsk)${NO_COLOR}"
echo
fi
# -----------------------------------------------------------------------------------------------------------
# Create a payment address from the extended public payment verification key
cat ${OUTPUT_DIR}/payment.xvk | cardano-address address payment --network-tag ${NETWORK_TAG} > ${OUTPUT_DIR}/payment.addr
# Generate a delegated payment address from the payment address and the extended public stake verification key
cat ${OUTPUT_DIR}/payment.addr | cardano-address address delegation $(cat ${OUTPUT_DIR}/stake.xvk) > ${OUTPUT_DIR}/delegated-payment.addr
# Generate a stake address from the extended public stake verification key
cat ${OUTPUT_DIR}/stake.xvk | cardano-address address stake --network-tag ${NETWORK_TAG} > ${OUTPUT_DIR}/stake.addr
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo "Payment address: ${OUTPUT_DIR}/payment.addr"
echo -e ${ADDRESS_COLOR}"$(cat ${OUTPUT_DIR}/payment.addr)${NO_COLOR}"
echo
echo "Delegated payment address: ${OUTPUT_DIR}/delegated-payment.addr"
echo -e ${ADDRESS_COLOR}"$(cat ${OUTPUT_DIR}/delegated-payment.addr)${NO_COLOR}"
echo
echo "Stake address: ${OUTPUT_DIR}/stake.addr"
echo -e ${ADDRESS_COLOR}"$(cat ${OUTPUT_DIR}/stake.addr)${NO_COLOR}"
echo
fi
# -----------------------------------------------------------------------------------------------------------
# Convert cardano-address extended keys to Shelley-format keys.
cardano-cli key convert-cardano-address-key \
--shelley-payment-key \
--signing-key-file ${OUTPUT_DIR}/payment.xsk \
--out-file ${OUTPUT_DIR}/payment.eskey
cardano-cli key verification-key \
--signing-key-file ${OUTPUT_DIR}/payment.eskey \
--verification-key-file ${OUTPUT_DIR}/payment.evkey
cardano-cli key non-extended-key \
--extended-verification-key-file ${OUTPUT_DIR}/payment.evkey \
--verification-key-file ${OUTPUT_DIR}/payment.vkey
cardano-cli key convert-cardano-address-key \
--shelley-stake-key \
--signing-key-file ${OUTPUT_DIR}/stake.xsk \
--out-file ${OUTPUT_DIR}/stake.eskey
cardano-cli key verification-key \
--signing-key-file ${OUTPUT_DIR}/stake.eskey \
--verification-key-file ${OUTPUT_DIR}/stake.evkey
cardano-cli key non-extended-key \
--extended-verification-key-file ${OUTPUT_DIR}/stake.evkey \
--verification-key-file ${OUTPUT_DIR}/stake.vkey
if [ ${FLAGS_verbose} = ${FLAGS_TRUE} ] ; then
echo "Extended payment signing key: ${OUTPUT_DIR}/payment.eskey"
jq . ${OUTPUT_DIR}/payment.eskey
echo
echo "Extended payment verification key: ${OUTPUT_DIR}/payment.evkey"
jq . ${OUTPUT_DIR}/payment.evkey
echo
echo "Payment verification key: ${OUTPUT_DIR}/payment.vkey"
jq . ${OUTPUT_DIR}/payment.vkey
echo
echo "Extended stake signing key: ${OUTPUT_DIR}/stake.eskey"
jq . ${OUTPUT_DIR}/stake.eskey
echo
echo "Extended stake verification key: ${OUTPUT_DIR}/stake.evkey"
jq . ${OUTPUT_DIR}/stake.evkey
echo
echo "Stake verification key: ${OUTPUT_DIR}/stake.vkey"
jq . ${OUTPUT_DIR}/stake.vkey
echo
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment