Skip to content

Instantly share code, notes, and snippets.

@ilap
Last active November 24, 2022 03:01
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save ilap/3fd57e39520c90f084d25b0ef2b96894 to your computer and use it in GitHub Desktop.
Save ilap/3fd57e39520c90f084d25b0ef2b96894 to your computer and use it in GitHub Desktop.
Extracting Pool Staking keys from Daedalus/Yoroi wallet

Intorduction

DISCLAIMER: NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK

UPDATED: 14:08am AEST 29/Sept/2020, The IDX was incorrectly used. Fixed now

There are two keypairs that are required to register a pool:

  1. reward account (costs and rewards) and
  2. owner stake (pledge) keypair.

Note: these keys can be the same when the owner and the operator are the same.

So, that would be nice if the pool operators/owner could track and controle their pool rewards from any wallet (Daedalus, Yoroi or any other wallet) that support stakings.

The aim of this document is achieve this, by using some Shelley wallet-, address- and key related tools.

The only problem is how to extract the staking key(s) from a wallet. Luckily there is the IOHK's cardano-wallet repo that contains cardano-address that can be used for this as it contains the BIP39, BIP32-ED25519 and master key generation functionalities which are required to achive this.

Create wallet and extract staking keys from it.

The following steps are required for extracting staking keys from a wallet:

  1. Generate a wallet and save the 15-word length mnemonic as usual.
  2. Use the mnemonic /w cardano-address cli to generate the master (root) key.
  3. Derive the staking signing key from the master key using the 1852H/1815H/0H/2/0 (cardano-address derivation format).
  4. generate the staking verification key from the above signing key
  5. Used the extracted keypair or keypairs (if additional wallets is created for owner and operator) for pool registration and or pledge.

To crate a wallet you can use the Daedalus 1.6.0-STN5 for creating wallet(s) as the address structure won't be changed on the mainnet. And write down and save that/those mnemonic.

Downloads

Cardano-wallet binaries

From latest releases https://github.com/input-output-hk/cardano-wallet/releases or from Hydra: https://hydra.iohk.io/build/3662127/download/1/cardano-wallet-shelley-2020.7.28-linux64.tar.gz https://hydra.iohk.io/build/3662151/download/1/cardano-wallet-shelley-2020.7.28-macos64.tar.gz https://hydra.iohk.io/build/3662143/download/1/cardano-wallet-shelley-2020.7.28-win64.zip

Script

#!/bin/bash 

CADDR=${CADDR:=$( which cardano-address )}
[[ -z "$CADDR" ]] && { echo "cardano-address cannot be found, exiting..." >&2 ; exit 127; }

CCLI=${CCLI:=$( which cardano-cli )}
[[ -z "$CCLI" ]] && { echo "cardano-cli cannot be found, exiting..." >&2 ; exit 127; }

BECH32=${BECH32:=$( which cardano-cli )}
[[ -z "$BECH32" ]] && { echo "bech32 cannot be found, exiting..." >&2 ; exit 127; }

# Only 24-word length mnemonic is supported only
[[ "$#" -ne 26 ]] && {
       	echo "usage: `basename $0` <change index e.g. 0/1 external/internal>  <ouptut dir> <24-word length mnemonic>" >&2 
       	exit 127
}

GEN_FILE=${GEN_FILE:="$HOME/conf/shelley-genesis.json"}
[[ ! -f "$GEN_FILE" ]] && { echo "genesis file does not exit, exiting..." >&2 ; exit 127; }

IDX=$1
shift 

OUT_DIR="$1"
[[ -e "$OUT_DIR"  ]] && {
       	echo "The \"$OUT_DIR\" is already exist delete and run again." >&2 
       	exit 127
} || mkdir -p "$OUT_DIR" && pushd "$OUT_DIR" >/dev/null

shift
MNEMONIC="$*"

# Generate the master key from mnemonics and derive the stake account keys 
# as extended private and public keys (xpub, xprv)
echo "$MNEMONIC" |\
"$CADDR" key from-recovery-phrase Shelley > root.prv

cat root.prv |\
"$CADDR" key child 1852H/1815H/0H/2/0 > stake.xprv

cat root.prv |\
"$CADDR" key child 1852H/1815H/0H/$IDX/0 > payment.xprv

# XPrv/XPub conversion to normal private and public key, keep in mind the 
# keypars are not a valind Ed25519 signing keypairs.
NW=$(jq '.networkId' -r "$GEN_FILE")
NW_ID=$(jq '.networkMagic' -r "$GEN_FILE")

echo "Generating $NW wallet..."
if [ "$NW" == "Testnet" ]; then
  NETWORK=0
  MAGIC="--testnet-magic $NW_ID"
  CONV="bech32 | bech32 addr_test"
else
  NETWORK=1
  MAGIC="--mainnet"
  CONV="cat"
fi

cat payment.xprv |\
"$CADDR" key public | tee payment.xpub |\
"$CADDR" address payment --network-tag $NETWORK |\
"$CADDR" address delegation $(cat stake.xprv | "$CADDR" key public | tee stake.xpub) |\
tee base.addr_candidate |\
"$CADDR" address inspect
echo "Generated from 1852H/1815H/0H/$IDX/0"
if [  "$NW" == "Testnet" ]; then
  cat base.addr_candidate | bech32 | bech32 addr_test > base.addr_candidate_test
  mv base.addr_candidate_test base.addr_candidate
fi
cat base.addr_candidate 
echo


SESKEY=$( cat stake.xprv | bech32 | cut -b -128 )$( cat stake.xpub | bech32)
PESKEY=$( cat payment.xprv | bech32 | cut -b -128 )$( cat payment.xpub | bech32)

cat << EOF > stake.skey
{
    "type": "StakeExtendedSigningKeyShelley_ed25519_bip32",
    "description": "",
    "cborHex": "5880$SESKEY"
}
EOF

cat << EOF > payment.skey
{
    "type": "PaymentExtendedSigningKeyShelley_ed25519_bip32",
    "description": "Payment Signing Key",
    "cborHex": "5880$PESKEY"
}
EOF

"$CCLI" shelley key verification-key --signing-key-file stake.skey --verification-key-file stake.evkey
"$CCLI" shelley key verification-key --signing-key-file payment.skey --verification-key-file payment.evkey

"$CCLI" shelley key non-extended-key --extended-verification-key-file payment.evkey --verification-key-file payment.vkey
"$CCLI" shelley key non-extended-key --extended-verification-key-file stake.evkey --verification-key-file stake.vkey


"$CCLI" shelley stake-address build --stake-verification-key-file stake.vkey $MAGIC > stake.addr
"$CCLI" shelley address build --payment-verification-key-file payment.vkey $MAGIC > payment.addr
"$CCLI" shelley address build \
    --payment-verification-key-file payment.vkey \
    --stake-verification-key-file stake.vkey \
    $MAGIC > base.addr

echo "Important the base.addr and the base.addr_candidate must be the same"
diff -s base.addr base.addr_candidate
cat base.addr base.addr_candidate

popd >/dev/null
@adamsthws
Copy link

adamsthws commented Aug 8, 2021

Thanks for the awesome tool! Quick question if I may...
I'm attempting to use the 24 word mnemonic from a ledger hw wallet, however, the script's generated address doesn't match any of my ledger addresses shown in Daedalus... Are the ledger mnemonics compatible?

For ledger use this, as the master key generation is different.
But, I think it needs revised and updated, also be very careful do everything in air gapped device after you downloaded everything.
https://gist.github.com/ilap/5af151351dcf30a2954685b6edc0039b

Lovely, thankyou for the guidance, I'll let you know how it goes.
-Adam

I'm happy to report this worked perfectly for the ledger method, thankyou ilap, you're a star!

@ilap
Copy link
Author

ilap commented Aug 8, 2021

@adamsthws 👌

@00TIA
Copy link

00TIA commented Jan 18, 2022

During the process of creating the stake pool at the command cardano-cli query utxo \ --address $(cat payment.addr) \ --mainnet I get this output.
any help is appreciated
Schermata 2022-01-18 alle 21 54 54

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment