Part 2: Technical Overview

Deposits and Withdrawals

Making a Deposit

  • Initial deposits create a validator's record.
  • Top-up deposits increase an existing validator's balance.
  • Making a deposit involves sending a transaction to the deposit contract.
  • The Ethereum Launchpad provides a nice interface for this, although alternatives exist.
  • The deposit CLI tool, among others, can create deposit data and BLS keystores.

Introduction

This is not a how-to guide, so I'll only consider the main tools and workflows as an introduction to the ideas.

The Ethereum Foundation's Staking Launchpad is the entry point for many solo stakers. Large operations might use smart contracts to submit deposits in batches, but we will focus on a single deposit of 32 ETH.

The Launchpad will guide you towards using the staking deposit CLI tool1. It is strongly recommended that you run the deposit CLI tool offline, possibly air-gapped and on a live-booted machine. This is to keep your mnemonic seed phrase as safe as possible2.

Initial deposits

When making an initial deposit – that is, for a validator that does not exist yet – the staking CLI can be run interactively as follows.

./deposit new-mnemonic --execution_address '0x00....09'

By default, without the --execution-address parameter, the staking CLI will generate old-style BLS withdrawal credentials. You'll want to be using the new-style Eth1 withdrawal credentials, so specify the address of an Ethereum account that you control here. If you don't do it now, you will need to change it later to receive your rewards and retrieve your stake.

The tool will generate a new 256 bit seed using your machine's randomness and convert it into a 24 word mnemonic phrase based on the BIP-39 standard. A single mnemonic seed phrase can be used to generate a large number of BLS12-381 private–public key pairs, and hence a large number of validators. Keep this mnemonic phrase very safely somewhere, and never online. You will need it if you ever need to recreate your validator keys, and when you exit your validator.

You can generate keystores for multiple validators at once, based on the same mnemonic. Just tell the deposit CLI how many. Each will need its own 32 ETH stake, however.

After running through all the prompts and confirmations, the deposit CLI will generate some files. There will be a single deposit_data-xxx.json file and one keystore-m_12381_3600_0_0_n_xxx.json files per validator you generated.

Deposit data

The deposit_data-xxx.json file is part of the Launchpad's workflow; other tools may have different ways to do this. When you submit the deposit data file to the Launchpad, it will create a deposit transaction for each validator, which you sign with your normal Ethereum wallet, thereby sending 32 ETH to the deposit contract.

The file contains a section like this for each validator (I've truncated some lines for convenience).

{
  "pubkey": "a70d57e5fd4615bd3110a709be82be7a8b966fe881290f2738e4d8d0b38f39fe...",
  "withdrawal_credentials": "0100000000000000000000000001020304050607080900010203040506070809",
  "amount": 32000000000,
  "signature": "a6821877521df6ea65e7458fd599ef6430d23f64789cf7d89a75658eccdaf841...",
  "deposit_message_root": "047eb9f043b4cd464084c44db76ddb937e3fda11a63fde59a6149f74b8c50685",
  "deposit_data_root": "5a05c42ace9518a92c5ec950e6f58a6fd490a06b7619370d1b700d8d93b2cbbe",
  "fork_version": "00000000",
  "network_name": "mainnet",
  "deposit_cli_version": "2.5.0"
}

The fields are as follows.

  • pubkey is generated from the secret key that was generated from your mnemonic. It is the unique identity of the validator on the consensus layer. You can look it up on the Beaconcha.in explorer, for example.
  • withdrawal_credentials will begin with 01 if you specified the execution_address, and end with the 40 hexadecimal digits of the Ethereum account that withdrawals will go to. If you did not specify an execution_address, then withdrawal_credentials will begin 00 and be followed by a BLS withdrawal commitment.
  • amount is in Gwei. 32000000000 is 32 ETH.
  • signature is a BLS signature over the previous three fields, using your secret signing key.
  • deposit_message_root is the actual data that is signed with signature. It is the hash tree root of the DepositMessage object. It's technically redundant as it can be recalculated easily, but the Launchpad uses it as a checksum to validate that the submitted data and signature all validate correctly.
  • deposit_data_root is the hash tree root of the DepositData object created from the first four fields above (i.e. deposit message plus its signature). This is used as a checksum by the deposit contract.
  • fork_version specifies which chain the deposit is for. The fork_version is encoded into the signature so that deposits are valid only on the intended chain. The chain's GENESIS_FORK_VERSION is always used when signing deposits.

The remaining fields are just administrative records.

Keystores

Also generated by the deposit CLI is a keystore file for each validator. This contains the validator's encrypted secret key. The keystore will be used by the staker's client software and needs to be installed as per the client's instructions. The keystore contents are protected by the password provided when the deposit CLI was run, which also needs to be provided to the staking client. Each client handles this differently, so consult the docs.

Example keystore

A keystore file has contents like the below. You can see that the pubkey matches the pubkey in the deposit data file, above. I won't go into the details of this, but the format is described in ERC-2335. The derivation path parameter is discussed in ERC-2334.

{
  "crypto": {
    "kdf": {
      "function": "scrypt",
      "params": {
        "dklen": 32,
        "n": 262144,
        "r": 8,
        "p": 1,
        "salt": "d6679024b3693066eba27bbe7c2269fc62c98a1accf225c916d6eafb24abcdae"
      },
      "message": ""
    },
    "checksum": {
      "function": "sha256",
      "params": {},
      "message": "402eb9c5d6042f354bb8013ed19019b9d8cfa7deed1ed44eb2c0680615df1b13"
    },
    "cipher": {
      "function": "aes-128-ctr",
      "params": {
        "iv": "4ced4174acc07417f34106eb1cb5c685"
      },
      "message": "e7adc1ab79c2870fccd87b9cbd09830fcf0654cccca944ff3c9c26a1f6fb10b5"
    }
  },
  "description": "",
  "pubkey": "a70d57e5fd4615bd3110a709be82be7a8b966fe881290f2738e4d8d0b38f39fe..."
  "path": "m/12381/3600/0/0/0",
  "uuid": "7b25b4a7-9241-4ad8-9540-f0ed096f30cd",
  "version": 4
}

A key derivation function (KDF) is used to protect the secret key with a password. The KDF is designed to make it computationally infeasible to decrypt the secret key by brute-force.

The deposit CLI uses the Scrypt KDF which, by design, is slow and uses a lot of memory, about 300MB per key. This is fine for a solo-staker loading one or two keys, but can become a significant bottleneck for large staking services loading hundreds or thousands of keys at startup.

ERC-2335 keystores also support PBKDF2 as the KDF, which is much faster and less memory intensive. Depending on one's appetite for trading key security for loading speed, PBKDF2 may be preferable. The ethdo and eth-staking-smith tools are able to generate keystores using PBKDF2.

Top-up deposits

Top-ups are deposits for validators that already exist. You might want to top-up a validator if its effective balance falls below 32 ETH in order to restore it to maximum effectiveness.

The Staking Launchpad provides a top-up interface. You don't need access to your keystore or mnemonic to make a top-up deposit. In fact, anyone can top-up any validator at any time.

The transaction that gets sent to the deposit contract for a top-up is essentially the same as the transaction for an initial deposit, with the following differences:

  • the public key must match the public key of an existing validator,
  • the signature is not checked, and can be an "empty" dummy signature3, and
  • the withdrawal credentials are ignored.

It is possible to build up a validator's stake over time, with an initial deposit that's less than 32 ETH, followed by one or more top-up deposits. The validator will become active when its effective balance reaches 32 ETH. However, if you plan to do this, watch out for a tricky edge case involving hysteresis when the final top-up is 1 ETH.

See also

The Ethereum.org website has more information about solo staking, with a comparison of alternatives to the deposit CLI tool.

If you want to play around with consensus layer keys and wallets, the ethdo tool is extremely useful. It has been audited, and has a huge range of features as well as handling the basics.

Three ERC standards have been proposed in relation to key handling for the consensus layer.


  1. Eth-staking-smith is an alternative. I have not used it and cannot vouch for it, though the source is legit. It has the interesting feature of being able to use PBKDF2 as the key derivation function - see below.
  2. It used to be more important to protect your mnemonic as it controlled your withdrawal credentials as well as your signing key. Nowadays it will normally be used only for your signing key - an attacker can do you less harm with that than with the withdrawal credentials.
  3. It seems that block explorers do not know this and can incorrectly mark top-up transactions as invalid.

Created by Ben Edgington. Licensed under CC BY-SA 4.0. Published 2023-09-29 14:16 UTC. Commit ebfcf50.