# Withdrawals In this document I will include all the methods to make full withdrawals with the different clients and also show some examples about how to do a BLS to Execution change with `ethdo` tool. ### General assumptions Instead of using a long public key in all the queries, I just refer to it as `$validator`. ```shell= export validator="0xb37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46" ``` We assume for full withdrawal the withdrawal credential of type `0x01`. If you have an address that is `0x00`, then proceed to the bottom of the doc where it discusses how to convert a BLS (`0x00`) address into an execution address (`0x01`) All of our beacon nodes use port 4000 for API communication. ## All clients ### Using ethdo We will generate the withdrawal data with `ethdo`, inspect it locally and then submit it to any client of our choice. To do so, we must first create a wallet with `ethdo`: ``` ethdo wallet create \ --base-dir=<CHOOSE_WITHDRAWALS_DATA_FOLDER_LOCATION> \ --type=hd --wallet=withdrawal-validators \ --mnemonic="<VALIDATORS_MNEMONIC>" \ --wallet-passphrase="<CHOOSE_WALLET_PASSPHRASE>" ``` We then create an account using that wallet: ``` ethdo account create \ --base-dir=<WITHDRAWALS_DATA_FILE_LOCATION> \ --account=withdrawal-validator \ --wallet-passphrase="<WALLET_PASSPHRASE>" \ --passphrase="$WALLET_PASSPHRASE" \ --path="m/12381/3600/<INDEX_USUALLY_0>/0/0" ``` Now we can create the exit data with: ``` ethdo validator exit \ --base-dir=<WITHDRAWALS_DATA_FILE_LOCATION> \ --json --account=withdrawal-validator \ --passphrase="<WALLET_PASSPHRASE>" \ --connection=<ENTER BEACON API URL> > <WITHDRAWALS_DATA_FILE_LOCATION>/withdrawal.json ``` Manually inspect the `withdrawal.json` file and submit the withdrawal with the following: ``` ethdo validator exit \ --signed-operation=$(cat <WITHDRAWALS_DATA_FILE_LOCATION>/withdrawal.json) \ --connection=<ENTER BEACON API URL> ``` ## Lodestar Following example shows how to make a full withdrawal with lodestar client: ```shell= # Stop validator so the wallet file is no longer locked docker stop validator && \ docker run --rm -it \ --network host \ -v /home/devops:/data \ chainsafe/lodestar:next \ validator voluntary-exit \ --pubkeys $validator \ --dataDir /data/validator \ --paramsFile /data/custom_config_data/config.yaml \ --server http://127.0.0.1:4000/ && \ docker start validator ``` ### Lodestar exit procedure ```shell= ? Confirm to exit pubkeys at epoch 12 from network withdrawals-testnet? 0xb37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46 1232 active_ongoing Yes Submitted voluntary exit for 0xb37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46 1/1 ``` ## Lighthouse Following example shows how to make a full withdrawal with lighthouse client: ```shell= docker run --rm -it \ --network host \ -v /home/devops/validator:/validatordata \ -v /home/devops/custom_config_data:/custom_config_data \ sigp/lighthouse:capella \ lighthouse \ --testnet-dir="/custom_config_data" \ --datadir="/validatordata" \ account validator exit \ --keystore /validatordata/validators/$validator/voting-keystore.json \ --password-file /validatordata/secrets/$validator \ --beacon-node=http://127.0.0.1:4000 ``` ### Lighthouse exit procedure ```shell= Enter the exit phrase from the above URL to confirm the voluntary exit: Exit my validator Successfully validated and published voluntary exit for validator 0xb37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46 Voluntary exit has been accepted into the beacon chain, but not yet finalized. Finalization may take several minutes or longer. Before finalization there is a low probability that the exit may be reverted. Current epoch: 1147, Exit epoch: 1152, Withdrawable epoch: 1153 Please keep your validator running till exit epoch Exit epoch in approximately 1920 secs ``` ## Teku ```shell= docker run --rm -it\ --network host \ -v /home/devops/validator:/validatordata \ consensys/teku:develop \ voluntary-exit \ --beacon-node-api-endpoint=http://127.0.0.1:4000 \ --validator-keys=/validatordata/keys/$validator.json:/validatordata/secrets/$validator.txt ``` ### Teku exit procedure ```shell= Loading configuration... Exits are going to be generated for validators: ba0008f Epoch: 11 These validators won't be able to be re-activated, and withdrawals aren't likely to be possible until Phase 2 of eth2 Mainnet. Are you sure you wish to continue (yes/no)? yes Exit for validator ba0008f submitted. ``` ## Prysm ```shell= docker run --rm -it \ --network host \ -v /home/devops/validator:/wallet \ gcr.io/prysmaticlabs/prysm/validator:capella \ accounts voluntary-exit \ --wallet-dir=/wallet/wallet \ --wallet-password-file=/wallet/wallet_pass.txt \ --beacon-rpc-provider=127.0.0.1:4001 \ --force-exit \ --accept-terms-of-use ``` ### Prysm exit procedure > **_NOTE:_** Prysm doesn't pass the validator public key as an argument but rather you have to interactively select which validator you would like to exit from the wallet. ```shell= [Selected account] 0 | immensely-polished-minnow | 0xb37101f32ee0 Done with selections Are you sure you want to perform a voluntary exit on 1 account? (0xb37101f32ee0) Y/N: Y ===============IMPORTANT=============== Withdrawing funds is not yet possible. Please navigate to the following website and make sure you understand the current implications of a voluntary exit before making the final decision: https://docs.prylabs.network/docs/wallet/exiting-a-validator/#withdrawal-delay-warning If you still want to continue with the voluntary exit, please input a phrase found at the end of the page from the above URL: Exit my validator [2022-12-06 15:12:08] INFO accounts: Voluntary exit was successful for the accounts listed. URLs where you can track each validator's exit: https://beaconcha.in/validator/b37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46 publicKeys=0xb37101f32ee0 ``` ## Nimbus ```shell= docker run --rm -it \ --network host \ -v /home/devops/validator:/validatordata \ -v /home/devops/beacon:/data \ ethpandaops/nimbus:unstable \ nimbus_beacon_node deposits exit \ --rest-url=127.0.0.1:4000 \ --data-dir=/data \ --validator=$validator ``` ## Output In order to verify that the full withdrawal was successful you can check the status of the validator with a Beacon API call: ```shell= curl http://127.0.0.1:4000/eth/v1/beacon/states/head/validators/$validator | jq . ``` ### Expected output before withdrawal: ```json= { "execution_optimistic": false, "data": { "index": "55", "balance": "32000001414", "status": "active_ongoing", "validator": { "pubkey": "0xb37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46", "withdrawal_credentials": "0x010000000000000000000000e7c180eada8f60d63e9671867b2e0ca2649207a8", "effective_balance": "32000000000", "slashed": false, "activation_eligibility_epoch": "0", "activation_epoch": "0", "exit_epoch": "18446744073709551615", "withdrawable_epoch": "18446744073709551615" } } } ``` > **_NOTE:_** The most important fields are `data.balance`, `data.status`, `data.validator.withdrawal_credentials`. ### Expected output after full withdrawal is triggered ```json= { "execution_optimistic": false, "data": { "index": "304", "balance": "32000000000", "status": "active_exiting", "validator": { "pubkey": "0xb37101f32ee0d54ca6958764d21bd416b397c0a199d5ff447b8cd41bba89d78da9d12622134cc8a3cafe30daad85cd46", "withdrawal_credentials": "0x010000000000000000000000e7c180eada8f60d63e9671867b2e0ca2649207a8", "effective_balance": "32000000000", "slashed": false, "activation_eligibility_epoch": "0", "activation_epoch": "0", "exit_epoch": "1152", "withdrawable_epoch": "1153" } } } ``` > **_NOTE:_** `data.validator.exit_epoch` and `data.validator_withdrawable_epoch` has now change to the exit and withdrawable epoch one epoch after the exit is triggered. Also `data.status` is now in "active_exiting state". Any balance above 32 is partially withdrawn to the address that is set as withdrawal address. ### Expected output after the full withdrawal is possible ```json= { "execution_optimistic": false, "data": { "index": "304", "balance": "0", "status": "withdrawal_possible", "validator": { "pubkey": "0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd", "withdrawal_credentials": "0x010000000000000000000000e7c180eada8f60d63e9671867b2e0ca2649207a8", "effective_balance": "32000000000", "slashed": false, "activation_eligibility_epoch": "0", "activation_epoch": "0", "exit_epoch": "1152", "withdrawable_epoch": "1153" } } } ``` > **_NOTE:_** `data.status` is now "withdrawal_possible". At this point the `data.balance` has reduced to 0, while the `data.validator.effective_balanace` is still at 32ETH. This is the state between `exit_epoch` and `withdrawable_epoch+1`. Once we are in epoch 1154 (`withdrawable_epoch` is justified) the status will go to the next state. ### Expected output after the full withdrawal is complete ```json= { "execution_optimistic": false, "data": { "index": "304", "balance": "0", "status": "withdrawal_done", "validator": { "pubkey": "0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd", "withdrawal_credentials": "0x010000000000000000000000e7c180eada8f60d63e9671867b2e0ca2649207a8", "effective_balance": "0", "slashed": false, "activation_eligibility_epoch": "0", "activation_epoch": "0", "exit_epoch": "1152", "withdrawable_epoch": "1153" } } } ``` > **_NOTE:_** `data.status` is now "withdrawal_done". The balance and effective balance are both 0. At this point the full withdrawal is done. At this point its possible that the validator was part of a sync committe which would result in a non 0 balance and further receive rewards until the sync committee is over. These smaller amounts will be cleared by additional partial withdrawal sweeps. # BLS to execution with ethdo The most simple way to use [ethdo](https://github.com/wealdtech/ethdo) to convert a BLS address (`0x00`) to execution address (`0x01`) and thus enable withdrawals is to use the mnemonic. (Not advised to do this on an online machine as its basically exposing your mnemonic to an internet exposed machine, but as its a testnet nobody really cares) Still to ensure that mnemonics aren't saved in the shell history on a shared machine, you can use `export HISTIGNORE=' *'` and then every command following that which begins with an empty space will be ignored by history file. On our testnet our validators get created with the same seed phrase and thus the only way to refer to them is by the derivation path. In the following example I'll change the address for the validator at index 50. (Thus using the derivation path 50) so `'m/12381/3600/50/0/0'`. ### Expected output before BLS to execution change has happened In order to check what the withdrawal crednetial is for validator at index 50 you can make the following request: ```shell= docker run --rm -it --network=host \ wealdtech/ethdo \ --connection 127.0.0.1:4000 \ validator info \ --validator=50 \ --verbose ``` Output: ```shell= Index: 50 Activation epoch: 0 Public key: 0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd Status: active_ongoing Balance: 32.005803277 Ether Effective balance: 32 Ether Withdrawal credentials: 0x005293868ccd6fc919d0ddee6cc47f0ff7e2a1c2d5ea3de2b54cee6ae5fe2fbc ``` At this point the withdrawal credentials is a `0x00` format. Thus its not an address that you can use for withdrawals. ### BLS Change to execution command for a specific validator (50) ```shell= docker run --rm -it \ --network=host \ wealdtech/ethdo \ --connection 127.0.0.1:4000 \ validator credentials set \ --mnemonic="change ... denial" \ --path='m/12381/3600/50/0/0' \ --withdrawal-address=0xE7c180eAdA8f60D63e9671867b2e0CA2649207A8 \ --verbose ``` Output: ```shell= Validator 50 found with public key 0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd at path m/12381/3600/50/0/0 ``` To check if the withdrawal credentials has changed for the given address, you can make the same call as previously and check: ```shell= Index: 50 Activation epoch: 0 Public key: 0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd Status: active_ongoing Balance: 32.000000707 Ether Effective balance: 32 Ether Withdrawal credentials: 0x010000000000000000000000e7c180eada8f60d63e9671867b2e0ca2649207a8 ``` Now the format follows the `0x01...e7c1...07a8` which is a valid withdrawal address. ### BLS Change offline To get the offline-preparation.json (containing all the account details) run the following command (should be ran on an online machine) ```shell= #Ensure the file offline file exists already, so it can be volume mounted to docker touch /home/devops/offline-preparation.json && \ docker run --rm -it \ -v /home/devops/offline-preparation.json:/app/offline-preparation.json \ --network=host \ wealdtech/ethdo \ --connection 127.0.0.1:4000 \ validator credentials set \ --prepare-offline ``` Once you have the file, copy to an offline machine and run the following command: ```shell= touch /home/devops/change-operations.json && docker run --rm -it \ -v /home/devops/change-operations.json:/app/change-operations.json \ -v /home/devops/offline-preparation.json:/app/offline-preparation.json \ wealdtech/ethdo \ validator credentials set \ --offline \ --mnemonic="change ... denial" \ --path='m/12381/3600/50/0/0' \ --withdrawal-address=0xE7c180eAdA8f60D63e9671867b2e0CA2649207A8 \ ``` This will output the operation commands in `change-operation.json` Then copy this file back to an online machine and run: ```shell= docker run --rm -it \ -v /home/devops/change-operations.json:/app/change-operations.json \ -v /home/devops/offline-preparation.json:/app/offline-preparation.json \ --network=host \ wealdtech/ethdo \ --connection 127.0.0.1:4000 \ validator credentials set ``` > **_NOTE:_** If you don't supply the `offline-preparation.json` file then ethdo will fetch the entire set of validators from the beacon node and use that to check the validity of the signed operations before submitting them to the beacon node. #### BLS change request file content `change-operations.json` File that is produced on the offline machine looks like this: ```json [ { "message": { "validator_index":"50", "from_bls_pubkey":"0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd", "to_execution_address":"0xe7c180eada8f60d63e9671867b2e0ca2649207a8" }, "signature":"0x8a24a124e1c9438065b73a6dfc678e567432490293a3a3d71ffcd22d36a97a2299cf38daeacb26e4e4ae87caed82d5ba10a6533a745b021ced41e4c0e7aa5af95c04dce5e050123fe83a3dafff902cd9630c72503b4fd9d328cc2138faae6e25" }, ... ] ``` Wait a couple seconds, and run ```shell= docker run --rm -it --network=host \ wealdtech/ethdo \ --connection 127.0.0.1:4000 \ validator info \ --validator=50 \ --verbose ``` to verify that the withdrawal credentials have now changed to a `0x01` address. Output should be something like this: ```shell= Index: 50 Activation epoch: 0 Public key: 0xb94237c39741511f4b245cbb8226b2d7d50483d8f9133b2fc387cc73a333b4a1f2fa06bc12e85be1043c778f5c95f4fd Status: active_ongoing Balance: 32 Ether Effective balance: 32 Ether Withdrawal credentials: 0x010000000000000000000000e7c180eada8f60d63e9671867b2e0ca2649207a8 ``` > **_NOTE:_** You can see that the address is `0x01...E7c180eAdA8f60D63e9671867b2e0CA2649207A8` where the last 40 bytes are your withdrawal address prepended with 22 zeroes and a `0x01`. # Publish BLS change to Consensus Layer client You can publish your BLS change to your consensus layer client by the following command: ```shell= curl -d @change-operations.json -H "Content-Type: application/json" -X POST 127.0.0.1:4000/eth/v1/beacon/pool/bls_to_execution_changes ``` Please note that teku/nimbus does not support BLS change before capella is activated. With those clients you have to wait until the fork. Meanwhile, prysm/lodestar/lighthouse fully support BLS change queuing in local pool before capella. However, gossiping (spreading the message to different nodes) the BLS change message will take place once capella is activated. {%hackmd theme-dark %} <style> .markdown-body pre {background-color: #1E1E1E;border: 1px solid #555 !important;color: #73BCE0;border-radius:8px;/*border-radius:0px;*/ } .markdown-body pre .htmlembedded {color: #C8D4C8 !important; } .markdown-body pre .hljs-tag {color: #6D726E; } .markdown-body pre .token.keyword {color: #C586C0; } .markdown-body pre .token.string, .markdown-body pre .hljs-string {color: #C68362; } .markdown-body pre .hljs-comment, .markdown-body pre .token.comment {color: #6A9955; } .markdown-body pre .hljs-attr {color: #73BCE0; } .markdown-body pre .hljs-name {color:#569CD6; } .markdown-body pre .token.operator {color:#C8D4C8;background:transparent; } .markdown-body pre .token.property {color: #73BCE0; } .markdown-body pre .token.function {color: #DCDCAA; } .markdown-body pre .token.builtin {color: #34B294; } .markdown-body pre .token.number {color: #B5CEA8; } .markdown-body pre .token.constant {color: #3BC1FF; } .markdown-body pre .hljs-addition {color: #96D47D;background: #373D29; } .markdown-body pre .hljs-deletion {color: #E76A6A;background: #4B1818; } .markdown-body pre .hljs-selector-class {color: #D7BA5F; } .markdown-body pre .hljs-attribute {color: #9CDCFE; } .markdown-body pre .hljs-number {color: #C68362; } .markdown-body pre .hljs-meta {color: #2C7CD6; } </style>