Skip to main content

Validator Registration

Before running your node, you must register it in the Espresso Stake Table contract on Ethereum L1. This associates your consensus keys with a unique Ethereum address that will receive commission rewards.

warning

Registration requires an Ethereum L1 transaction. Ensure your account has enough ETH for gas.

Prerequisites

  • An Ethereum wallet with a mnemonic and enough ETH for gas
  • A running Ethereum L1 RPC endpoint (e.g. Infura, Alchemy)
  • The Espresso container image: ghcr.io/espressosystems/espresso-sequencer/sequencer:20260407
  • The staking CLI image: ghcr.io/espressosystems/espresso-network/staking-cli:main

Step 1: Generate Consensus Keys

Espresso nodes require two cryptographic key pairs for consensus:

Key TypePrivate Key PrefixPublic Key PrefixPurpose
BLSBLS_SIGNING_KEY~BLS_VER_KEY~Consensus voting (staking)
SchnorrSCHNORR_SIGNING_KEY~SCHNORR_VER_KEY~State signatures

Generate both key pairs using the built-in keygen utility:

docker run --rm -v $(pwd)/keys:/keys \
ghcr.io/espressosystems/espresso-sequencer/sequencer:20260407 \
keygen -o /keys

This creates a key file at ./keys/0.env containing:

ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY=BLS_SIGNING_KEY~<your-bls-private-key>
ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY=SCHNORR_SIGNING_KEY~<your-schnorr-private-key>

You will also see the corresponding public keys in the output. Save the BLS public key (BLS_VER_KEY~...) — you'll need it for the metadata file.

danger

NEVER share or expose your private keys (BLS_SIGNING_KEY~... and SCHNORR_SIGNING_KEY~...). Keep them secure. Only public keys (BLS_VER_KEY~..., SCHNORR_VER_KEY~...) are safe to share.

Step 2: Prepare Validator Metadata

The METADATA_URI points to a publicly hosted JSON file describing your validator. This metadata is displayed in the Espresso staking UI and helps delegators identify your node.

Required JSON Schema

Based on the Espresso source code and the staking CLI README, a custom metadata JSON file has the following structure:

{
"pub_key": "BLS_VER_KEY~...",
"name": "Your Validator Name",
"description": "High-performance Espresso validator operated by your team",
"company_name": "Your Team or Company",
"company_website": "https://your-domain.com",
"client_version": "20260407",
"icon": {
"14x14": {
"@1x": "https://your-domain.com/espresso/icon-14.png",
"@2x": "https://your-domain.com/espresso/[email protected]",
"@3x": "https://your-domain.com/espresso/[email protected]"
},
"24x24": {
"@1x": "https://your-domain.com/espresso/icon-24.png",
"@2x": "https://your-domain.com/espresso/[email protected]",
"@3x": "https://your-domain.com/espresso/[email protected]"
}
}
}
important

pub_key is the only required JSON field. It must be the BLS public verification key of the registering node and must keep the BLS_VER_KEY~ prefix. This value must match the BLS key derived from CONSENSUS_PRIVATE_KEY; otherwise staking-cli register-validator metadata validation fails with a metadata pub_key mismatch error.

warning

Do not put BLS_SIGNING_KEY~... or SCHNORR_VER_KEY~... in pub_key. The metadata key is the public BLS verification key only: BLS_VER_KEY~....

Metadata Field Reference

FieldRequiredNotes
pub_keyYesBLS public verification key, exactly BLS_VER_KEY~...
nameNoHuman-readable validator name
descriptionNoLonger validator description
company_nameNoTeam, company, or individual operator name
company_websiteNoValid URL
client_versionNoClient/image version, for example 20260407
iconNoOptional 14x14 and 24x24 image URLs with @1x, @2x, @3x entries

The current schema does not define operating_system, node_type, or network_type. Extra JSON fields are ignored by the current parser, but they should not be used in examples because the staking UI will not consume them.

Obtaining Your BLS Public Key

If you only have the private key and need to derive the public key:

docker run --rm \
ghcr.io/espressosystems/espresso-sequencer/sequencer:20260407 \
pub-key -l "BLS_SIGNING_KEY~your-private-key"

Use the resulting BLS_VER_KEY~... value verbatim in metadata:

{
"pub_key": "BLS_VER_KEY~output-from-pub-key-command"
}

Do not strip the BLS_VER_KEY~ tag. Espresso keys use tagged-base64 encoding, and the tag is part of the parseable key format.

Hosting the Metadata

Host the JSON file at any publicly accessible URL:

MethodExample URL
GitHub Rawhttps://raw.githubusercontent.com/yourorg/repo/main/metadata.json
S3 / R2https://your-bucket.s3.amazonaws.com/validator-metadata.json
Your domainhttps://blocknth.com/espresso/metadata.json

Preview and validate the metadata before registering:

docker run --rm \
ghcr.io/espressosystems/espresso-network/staking-cli:main \
staking-cli preview-metadata \
--metadata-uri "https://your-domain.com/validator-metadata.json"

Step 3: Register the Validator

Run the registration command using the staking CLI:

docker run \
-e L1_PROVIDER="https://mainnet.infura.io/v3/<API-KEY>" \
-e STAKE_TABLE_ADDRESS=0xCeF474D372B5b09dEfe2aF187bf17338Dc704451 \
-e MNEMONIC="your twelve or twenty four word mnemonic phrase here" \
-e ACCOUNT_INDEX=0 \
-e CONSENSUS_PRIVATE_KEY="BLS_SIGNING_KEY~your-bls-private-key" \
-e STATE_PRIVATE_KEY="SCHNORR_SIGNING_KEY~your-schnorr-private-key" \
ghcr.io/espressosystems/espresso-network/staking-cli:main \
staking-cli register-validator \
--commission 5.00 \
--metadata-uri "https://your-domain.com/validator-metadata.json"

Parameter Reference

ParameterDescription
L1_PROVIDEREthereum mainnet HTTP RPC URL
STAKE_TABLE_ADDRESS0xCeF474D372B5b09dEfe2aF187bf17338Dc704451 (Mainnet)
MNEMONICEthereum mnemonic (BIP-39) for deriving the registration address
ACCOUNT_INDEXBIP-44 derivation index (m/44'/60'/0'/0/{index}), starts from 0
CONSENSUS_PRIVATE_KEYBLS private key (BLS_SIGNING_KEY~...)
STATE_PRIVATE_KEYSchnorr private key (SCHNORR_SIGNING_KEY~...)
--commissionCommission rate in percentage (e.g. 5.00 = 5%, up to 2 decimal places)
--metadata-uriPublic URL to your validator metadata JSON

Successful Output

A successful registration looks like:

INFO staking_cli: Registering validator 0xYourAddress with commission 5.00 %
INFO staking_cli: Success! transaction hash: 0xabc123...
tip

You can verify the transaction on Etherscan by searching the transaction hash. The To address should be the Stake Table contract 0xCeF474D372B5b09dEfe2aF187bf17338Dc704451.

Important Rules

  • Each BLS key can only be registered once
  • Each Ethereum account (MNEMONIC + ACCOUNT_INDEX) can only register one validator
  • For multiple validators, use different ACCOUNT_INDEX values or different mnemonics
  • The Ethereum account must have enough ETH to pay for the L1 registration gas

Step 4: Delegate Stake

After registration, your node needs ESP tokens delegated to it to participate in consensus. You (or any token holder) can delegate using:

docker run \
-e MNEMONIC="delegator-mnemonic-phrase" \
-e ACCOUNT_INDEX=0 \
-e L1_PROVIDER="https://mainnet.infura.io/v3/<API-KEY>" \
-e ESP_TOKEN_ADDRESS=0x031De51F3E8016514Bd0963d0B2AB825A591Db9A \
-e STAKE_TABLE_ADDRESS=0xCeF474D372B5b09dEfe2aF187bf17338Dc704451 \
ghcr.io/espressosystems/espresso-network/staking-cli:main \
staking-cli delegate --validator-address $VALIDATOR_ETH_ADDRESS --amount $DELEGATION_AMOUNT
info

The top 100 nodes by delegated stake form the active validator set each epoch (~24 hours). You must attract enough delegation to be in the top 100 to participate.

Managing Your Registration

Update Commission

Commission can be updated with the following constraints:

  • Limited to once per 7 days
  • Increases capped at 5% per update
  • Decreases have no limit
docker run \
-e L1_PROVIDER="https://mainnet.infura.io/v3/<API-KEY>" \
-e STAKE_TABLE_ADDRESS=0xCeF474D372B5b09dEfe2aF187bf17338Dc704451 \
-e MNEMONIC="your-mnemonic" \
-e ACCOUNT_INDEX=0 \
-e CONSENSUS_PRIVATE_KEY="BLS_SIGNING_KEY~..." \
-e STATE_PRIVATE_KEY="SCHNORR_SIGNING_KEY~..." \
ghcr.io/espressosystems/espresso-network/staking-cli:main \
staking-cli update-commission --new-commission 2.54

Deregister

To stop participating and remove all delegators:

docker run \
-e MNEMONIC="your-mnemonic" \
-e ACCOUNT_INDEX=0 \
-e L1_PROVIDER="https://mainnet.infura.io/v3/<API-KEY>" \
-e ESP_TOKEN_ADDRESS=0x031De51F3E8016514Bd0963d0B2AB825A591Db9A \
-e STAKE_TABLE_ADDRESS=0xCeF474D372B5b09dEfe2aF187bf17338Dc704451 \
ghcr.io/espressosystems/espresso-network/staking-cli:main \
staking-cli deregister-validator

Undelegate

To remove a delegation:

docker run \
-e MNEMONIC="delegator-mnemonic" \
-e ACCOUNT_INDEX=0 \
-e L1_PROVIDER="https://mainnet.infura.io/v3/<API-KEY>" \
-e ESP_TOKEN_ADDRESS=0x031De51F3E8016514Bd0963d0B2AB825A591Db9A \
-e STAKE_TABLE_ADDRESS=0xCeF474D372B5b09dEfe2aF187bf17338Dc704451 \
ghcr.io/espressosystems/espresso-network/staking-cli:main \
staking-cli undelegate --validator-address $VALIDATOR_ETH_ADDRESS --amount $AMOUNT

Ledger Support

The staking CLI supports Ledger hardware wallets for signing transactions. See the staking-cli README for details.