Created
February 18, 2021 22:36
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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