Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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 1853H/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
@MarcelKlammer

This comment has been minimized.

Copy link

@MarcelKlammer MarcelKlammer commented Jul 28, 2020

How? How do you use the keys then without converting them to cborHex?

@MarcelKlammer

This comment has been minimized.

Copy link

@MarcelKlammer MarcelKlammer commented Jul 28, 2020

I do see this commig: input-output-hk/cardano-node@2c8ed79

Which should add support for the above keys, right?

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Jul 29, 2020

I do see this commig: input-output-hk/cardano-node@2c8ed79

Which should add support for the above keys, right?

Yep, it's mandatory, cos if cardano-cli and ledger do not support signing transaction/certs /w bip32-ed25519 but only ed25519 keys, then we would be out of luck.

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Jul 29, 2020

How? How do you use the keys then without converting them to cborHex?

We need to convert to some skey/vkey format but that format must be supported by cardano-cli to sign the corresponding certs and witness the relevant transaction.

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Jul 29, 2020

I do see this commig: input-output-hk/cardano-node@2c8ed79

Which should add support for the above keys, right?

Done, you can try it. Even it would work /w cntools if you run like this:

# This is a randomly generated 15-word length mnemonic, You can use 24-word lengtn one too
mnem2addr.sh "$CNODE_HOME/priv/wallet/TestOperator" hope evolve shell lamp unfold lonely song evoke system candy hard need rally idle coral
{
    "stake_reference": "by value",
    "stake_key_hash": "27c334c87a7d3f423c23fa6cbd537caaf743e527c8ede06d3421859f",
    "address_style": "Shelley",
    "spending_key_hash": "a37af357a4375d82f96cc1513b083edc61919cbe0cbf9a20adfd5498",
    "network_tag": 1
}
Generated from 1852H/1815H/0H/{0,2}/0
addr1qx3h4u6h5sm4mqhednq4zwcg8mwxryvuhcxtlx3q4h74fxp8cv6vs7na8aprcgl6dj74xl927ap72f7gahsx6dppsk0s0c6af4
Important the base.addr and the base.addr_candidate must be the same
1c1
< addr1qx3h4u6h5sm4mqhednq4zwcg8mwxryvuhcxtlx3q4h74fxp8cv6vs7na8aprcgl6dj74xl927ap72f7gahsx6dppsk0s0c6af4
---
> addr1qx3h4u6h5sm4mqhednq4zwcg8mwxryvuhcxtlx3q4h74fxp8cv6vs7na8aprcgl6dj74xl927ap72f7gahsx6dppsk0s0c6af4
\ No newline at end of file
@papacarp

This comment has been minimized.

Copy link

@papacarp papacarp commented Jul 29, 2020

one change I'd recommend:

"$CADDR" address delegation $(cat stake.xprv | caddr key public | tee stake.xpub) |\

should be

"$CADDR" address delegation $(cat stake.xprv | "$CADDR" key public | tee stake.xpub) |\

Thank you for the script!

@MarcelKlammer

This comment has been minimized.

Copy link

@MarcelKlammer MarcelKlammer commented Jul 29, 2020

Yes, just got it to work on Ubuntu docker. Thanks for that.

@gitmachtl

This comment has been minimized.

Copy link

@gitmachtl gitmachtl commented Jul 29, 2020

You are my hero !

@hexadecimal-pool

This comment has been minimized.

Copy link

@hexadecimal-pool hexadecimal-pool commented Jul 29, 2020

Thank you!

@Straightpool

This comment has been minimized.

Copy link

@Straightpool Straightpool commented Jul 29, 2020

Got it to work on MacOS, thank you!

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Jul 29, 2020

one change I'd recommend:

"$CADDR" address delegation $(cat stake.xprv | caddr key public | tee stake.xpub) |\

should be

"$CADDR" address delegation $(cat stake.xprv | "$CADDR" key public | tee stake.xpub) |\

Thank you for the script!

Thx, updated.

@Straightpool

This comment has been minimized.

Copy link

@Straightpool Straightpool commented Jul 29, 2020

In case anyone is wondering, if you execute the script but get displayed zero funds in cntools, you need to spend a transaction fee for the fix:
Send your ADA from the Deadalus wallet you want to fix to the base.addr generated from the script to yourself. Deadalus will note a transaction with -0.168273 or something. Then cntools sees the funds on the base address which now can be spend to register pool and such.

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Jul 29, 2020

In case anyone is wondering, if you execute the script but get displayed zero funds in cntools, you need to spend a transaction fee for the fix:
Send your ADA from the Deadalus wallet you want to fix to the base.addr generated from the script to yourself. Deadalus will note a transaction with -0.168273 or something. Then cntools sees the funds on the base address which now can be spend to register pool and such.

Keep in mind that Daedalus and all other wallets are just a bag of the derivated addresses (see in GUI 's Receive tabs) and my script only uses the first (last in the list of Receive addresses) derived from m/1852'/1815'/0'/0/0. Also, you and others supposed to understand this at least before touch my script.

@Kaze-Stake

This comment has been minimized.

Copy link

@Kaze-Stake Kaze-Stake commented Jul 31, 2020

Does this script also work with a 24-word Daedalus shelley wallet?

@MarcelKlammer

This comment has been minimized.

Copy link

@MarcelKlammer MarcelKlammer commented Jul 31, 2020

Yes. Created los of wallets for pledging with it.

@Omegastaking

This comment has been minimized.

Copy link

@Omegastaking Omegastaking commented Aug 1, 2020

I am getting the following error message when trying to sign the transaction after using the Mnemonic generated keys. I am using a Daedalus 24 word Mnemonic.

cardano-cli shelley transaction submit
--tx-file tx.signed
--mainnet

Error while submitting tx: ApplyTxError [LedgerFailure (DelegsFailure (DelplFailure (DelegFailure (StakeKeyInRewardsDELEG (KeyHashObj (KeyHash "0f6c2b7908f60ba7e428793510f73892fac77e116c1ff939bb5f33e0"))))))

@johnnyboy93

This comment has been minimized.

Copy link

@johnnyboy93 johnnyboy93 commented Aug 2, 2020

so how do i use this?

@johnnyboy93

This comment has been minimized.

Copy link

@johnnyboy93 johnnyboy93 commented Aug 2, 2020

Yes. Created los of wallets for pledging with it.

can you please share a simple step by step how?
i have created a wallet in deadalus and want to use that for my cntools pledge

@laplasz

This comment has been minimized.

Copy link

@laplasz laplasz commented Aug 2, 2020

running the script on Ubuntu I got this error: bech32: command not found
my question is how to install this dependency? Thanks
Update: probably this: https://github.com/input-output-hk/bech32 can you please update the instructions with this address? thx

@laplasz

This comment has been minimized.

Copy link

@laplasz laplasz commented Aug 2, 2020

in the tutorial the payment.addr generated with both payment and stake verification keys..

cardano-cli shelley address build \
--payment-verification-key-file payment.vkey \
--stake-verification-key-file stake.vkey \
--out-file payment.addr \
--mainnet

but here the script generates the payment.addr only with the payment verification key

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

And the base.addr is generated in the way as payment.addr in the tutorial
for me this is a little bit confusing - is there a reason for the difference?

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Aug 2, 2020

in the tutorial the payment.addr generated with both payment and stake verification keys..

cardano-cli shelley address build \
--payment-verification-key-file payment.vkey \
--stake-verification-key-file stake.vkey \
--out-file payment.addr \
--mainnet

but here the script generates the payment.addr only with the payment verification key

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

And the base.addr is generated in the way as payment.addr in the tutorial
for me this is a little bit confusing - is there a reason for the difference?

The tutorial seems to me wrong but ttt I do not know the context so it might works properly, but the definitaion is misleading.
Read my article about keys etc to fully understand them on the guild-and operator's site here: https://cardano-community.github.io/guild-operators/#/Staking/Main

@MarcelKlammer

This comment has been minimized.

Copy link

@MarcelKlammer MarcelKlammer commented Aug 3, 2020

Since I use @gitmachtl scripts, I named the files like the scripts do, maybe it helps someone: https://gist.github.com/MarcelKlammer/2e44e64a0f0a743ce7400c1b863ced93

Write down the 24 words. Put them in Daedalus. The payment.addr is the very last addr. shown in the Receive section. If this addr matches the one generated, you are fine.

Keep in mind, that you must send ADA to that very last addr. Also, whenever you send ADA from that addr via Daedalus, send ALL ADA. Refund the last addr if necessary.

Good luck.

@Omegastaking

This comment has been minimized.

Copy link

@Omegastaking Omegastaking commented Aug 5, 2020

Hi MarcelKlammer,

With your code example are you able to successfully register your stakepool using the stake address. Then signing the transaction with both the payment and stake secret keys and then sending the signed transaction. If so, what specific steps are you following to complete this?

@coostendorp

This comment has been minimized.

Copy link

@coostendorp coostendorp commented Aug 6, 2020

Is this also working for testnet? When trying that the candidate.addr is different from the addr.

Example output:
addr1qpnvjr678ja9dxhqvf38r3j0a3ex94n02ak4n3062smzn82p9ps3606y9v35kpcma02p4uu6plrt9kl52ck8ms5krajqmphqqg
addr_test1qpnvjr678ja9dxhqvf38r3j0a3ex94n02ak4n3062smzn82p9ps3606y9v35kpcma02p4uu6plrt9kl52ck8ms5krajqfpr872

Strangely enough the first part is different, and the last 7 characters, the rest is the same. The code works fine for mainnet.
Also, the first output is correct and matches Daedalus' last receive key.

@ThaiTheo

This comment has been minimized.

Copy link

@ThaiTheo ThaiTheo commented Aug 7, 2020

@coostendorp, it works for testnet too.
But you have to change the variables which default to mainnet and you have to adjust the testnet-magic number.

@coostendorp

This comment has been minimized.

Copy link

@coostendorp coostendorp commented Aug 8, 2020

@coostendorp, it works for testnet too.
But you have to change the variables which default to mainnet and you have to adjust the testnet-magic number.

Somehow it does not work for me.

Changed network:

TESTNET=0
MAINNET=1
NETWORK=$TESTNET

Changed MAGIC:

TESTNET_MAGIC="--testnet-magic 1097911063"
MAINNET_MAGIC="--mainnet"
MAGIC="$TESTNET_MAGIC"

I probably missed something obvious if it works for everyone else, but right now cannot imagine what.

@MarcelKlammer

This comment has been minimized.

Copy link

@MarcelKlammer MarcelKlammer commented Aug 8, 2020

Didn't work for me either. It produced addr instead of addr_test prefixes and the last 4 hex values were different.

Not sure about the results.

Maybe just change the addr to addr_test and test those on testnet? Didn't test that.

@ThaiTheo

This comment has been minimized.

Copy link

@ThaiTheo ThaiTheo commented Aug 8, 2020

@MarcelKlammer then you didn't change the networks, like coostendorp did.

@ThaiTheo

This comment has been minimized.

Copy link

@ThaiTheo ThaiTheo commented Aug 8, 2020

@coostendorp it could be the Ubuntu version because its not a LTS version, but I don't know that for sure, I have tested it with Debian 10.

@ThaiTheo

This comment has been minimized.

Copy link

@ThaiTheo ThaiTheo commented Aug 8, 2020

Important notices to all who want to use this kind of wallet!

  1. The following command only sees the money which is send to the base address:
    cardano-cli shelley query utxo --address $(cat payment.addr) --mainnet
    So I suggest you only transfer the amount for the keyDeposit and the poolDeposit and some small amount of extra ADA because of the transaction fee's to the base address.
  2. Make sure you use a recovery phrase of 24 words to create a wallet. Recovery phrases which have either less or more words are not Shelley wallets.
  3. Be aware that besides sending ADA through the Daedalus wallet, you can now send ADA also with the payment.skey, so keep it in a safe place. (remember the payment.skey doesn't ask for a password for obvious reasons)
  4. The Windows version of the Daedalus wallet also comes with cardano-cli, so you can create keys and sign transactions on a Windows computer too if necessary.
@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Aug 9, 2020

Important notices to all who want to use this kind of wallet!

  1. The following command only sees the money which is send to the base address:
    cardano-cli shelley query utxo --address $(cat payment.addr) --mainnet
    So I suggest you only transfer the amount for the keyDeposit and the poolDeposit and some small amount of extra ADA because of the transaction fee's to the base address.
  2. Make sure you use a recovery phrase of 24 words to create a wallet. Recovery phrases which have either less or more words are not Shelley wallets.
  3. Be aware that besides sending ADA through the Daedalus wallet, you can now send ADA also with the payment.skey, so keep it in a safe place. (remember the payment.skey doesn't ask for a password for obvious reasons)
  4. The Windows version of the Daedalus wallet also comes with cardano-cli, so you can create keys and sign transactions on a Windows computer too if necessary.

Thx, also I have made it work /w Ledger Nano X/S hardware wallets. I will update this gist or create a new one.
But, it's a bit more complicated, so a different gist would be better to this.

@ilap

This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Aug 10, 2020

Here comes the gist for Ledger's as pledge/reward keys. Be very carefull, and do everyting only offline. NO ANY online process should be involved.
https://gist.github.com/ilap/5af151351dcf30a2954685b6edc0039b

@staking4ada

This comment has been minimized.

Copy link

@staking4ada staking4ada commented Aug 31, 2020

Hi, I am trying to get this done in Windows, in order for a partner to create a pledge address which he can manage himself. However, I cannot get past the bech32 conversion. Any chance someone can give a hint how to get past this step:

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

Is there any sign of IOHK working on an upcoming release which incorporates ilap's script functionality?

@ThaiTheo

This comment has been minimized.

Copy link

@ThaiTheo ThaiTheo commented Aug 31, 2020

Hi, I am trying to get this done in Windows
Is there any sign of IOHK working on an upcoming release which incorporates ilap's script functionality?

The script will not work in windows. Too many commands in the script that windows doesn't understand.
IOHK has more important things to do with the Daedulus wallet like the 1 to many delegation and hardware integration, so it will probably not be in there for a long time.

@staking4ada

This comment has been minimized.

Copy link

@staking4ada staking4ada commented Aug 31, 2020

no problem for everything else. I already successfully got to the bech32 part, and I think the part after it shouldn't be a problem either, so I probably only need an equivalent for the bech32 conversion.

@AndrewWestberg

This comment has been minimized.

Copy link

@AndrewWestberg AndrewWestberg commented Aug 31, 2020

no problem for everything else. I already successfully got to the bech32 part, and I think the part after it shouldn't be a problem either, so I probably only need an equivalent for the bech32 conversion.

I sent you a DM on telegram for usage under Windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.