# Anchor
> Open source Secret Shared Validator client. Built by the community, for the community.
## Advanced Networking
Anchor's networking stack is closely based on Lighthouse's. We refer to
[Lighthouse's page on Advanced Networking](https://lighthouse-book.sigmaprime.io/advanced_networking.html),
but want to outline several important differences:
* Currently, Anchor does not support UPnP.
* Anchor uses ports 12001 (UDP), 13001 (TCP), and 12002 (UDP) by default.
* Anchor does not yet support ENR auto-update - we therefore recommend manually setting publicly reachable ports via the
`--enr*-port` CLI parameters to advertise your node as reachable on the network.
## Advanced Usage
Want to get into the details of Anchor configuration? Looking for something not covered elsewhere?
This section provides detailed information about configuring Anchor for specific use cases, and tips about how things work under the hood.
If you are missing something here, we encourage you to [open an issue](https://github.com/sigp/anchor/issues) for us to add it!
* [Advanced Networking](advanced_networking): open your ports to have a diverse and healthy set of peers.
## Architectural Overview
This section provides developers an overview of the architectural design of Anchor. The intent of this
is to help gain an easy understanding of the client and associated code.
### Thread Model
Anchor is a multi-threaded client. There are a number of long standing tasks that are spawned when
Anchor is initialised. This section lists these high-level tasks and describes their purpose along
with how they are connected.
#### Task Overview
The following diagram gives a basic overview of the core tasks inside Anchor.
```mermaid
graph
A(Core Client) <--> B(HTTP API)
A(Core Client) <--> C(Metrics)
A(Core Client) <--> E(Execution Service)
A(Core Client) <--> F(Duties Service)
F <--> G(Processor)
H(Network) <--> G
I(QBFT) <--> G
```
The boxes here represent stand alone tasks, with the arrows representing channels between these
tasks. Memory is often shared between these tasks, but this is to give an overview of how the client
is pieced together.
The tasks are:
* **HTTP API** - A HTTP endpoint to read data from the client, or modify specific components.
* **Metrics** - Another HTTP endpoint designed to be scraped by a Prometheus instance. This provides
real-time metrics of the client.
* **Execution Service** - A service used to sync SSV information from the execution layer nodes.
* **Duties Service** - A service used to watch the beacon chain for validator duties for our known
SSV validator shares.
* **Network** - The p2p network stack (libp2p) that sends/receives data on the SSV p2p network.
* **Processor** - A middleware that handles CPU intensive tasks and prioritises the workload of the
client.
* **QBFT** - Spawns a QBFT instance and drives it to completion in order to reach consensus in an
SSV committee.
#### General Event Flow
Generally, tasks can operate independently from the core client. The main task that drives the
Anchor client is the duties service. It specifies when and what kind of validator duty we must be
doing at any given time.
Once a specific duty is assigned, a message is sent to the processor to start one (or many) QBFT instances for
this specific duty or duties. Simultaneously, we are awaiting messages on the network service. As
messages are received they are routed to the processor, validation is performed and then they are
routed to the appropriate QBFT instance.
Once we have reached consensus, messages are sent (via the processor) to the network to sign our
required duty.
A summary of this process is:
1. Await a duty from the duties service
2. Send the duty to the processor
3. The processor spins up a QBFT instance
4. Receive messages until the QBFT instance completes
5. Sign required consensus message
6. Publish the message on the p2p network.
An overview of how these threads are linked together is given below:
```mermaid
graph LR
A(Core Client) <--> B(Duties Service)
B <--> C(Processor)
C <--> D(Network)
C <--> E(QBFT)
```
import CliKeygenOptions from '../generated/cli-keygen-options.mdx'
### Keygen Command
The `keygen` command generates RSA keys for SSV operator identification
```bash
anchor keygen [OPTIONS]
```
#### Options
#### Examples
This will create an unencrypted `private_key.txt` file containing the newly generated
private key and a `public_key.txt` file with the BASE64 encoded public key used for
registering the operator.
```bash
anchor keygen
```
This will create a `encrypted_private_key.json` file encrypted with the provided password
and a `public_key.txt` file with the BASE64 encoded public key used for registering the
operator. The password must be provided via `--password-file` or interactively when running
Anchor.
```bash
anchor keygen --encrypt --data-dir /path/to/keys
```
Anchor will look for the key file in the default directory `~/.anchor/{network}`, or the directory specified by `--data-dir`.
import CliKeysplitOptions from '../generated/cli-keysplit-options.mdx'
### Keysplit Command
The `keysplit` command is used to split validator keys for distributed validation on the SSV network.
```bash
anchor keysplit [OPTIONS]
```
Where `` is one of:
* `manual` - Split keys with manually provided operator data
* `onchain` - Split keys using operator data from the blockchain
Both subcommands share these Options
##### Examples
Manual key splitting
```bash
anchor keysplit manual \
--keystore-path /path/to/validator_keystore.json \
--password-file /path/to/password.txt \
--owner 0x123abc... \
--operators 1,2,3,4 \
--output-path /path/to/output.json \
--nonce 0 \
--public-keys key1 key2 key3 key4 \
--network hoodi
```
Onchain key splitting
```bash
anchor keysplit onchain \
--keystore-path /path/to/validator_keystore.json \
--password-file /path/to/password.txt \
--owner 0x123abc... \
--operators 1,2,3,4 \
--output-path /path/to/output.json \
--rpc https://eth-mainnet.provider.com \
--network mainnet
```
These commands will generate a json file to be uploaded to the SSV network webapp when
registering a validator.
import CliNodeOptions from '../generated/cli-node-options.mdx'
### Node Command
The `node` command starts the anchor client as a SSV operator node.
```bash
anchor node [OPTIONS]
```
#### Options
##### Examples
```bash
anchor node \
--network hoodi \
--data-dir /data/anchor \
--beacon-nodes https://beacon1.example.com,https://beacon2.example.com \
--execution-rpc https://execution1.example.com,https://execution2.example.com \
--execution-ws wss://execution1.example.com \
--listen-addresses 10.0.0.10 \
--port 9100 \
--http \
--http-address 127.0.0.1 \
--http-port 9200 \
--unencrypted-http-transport \
--metrics \
--metrics-address 127.0.0.1 \
--metrics-port 9300 \
--password-file /path/to/your/password
```
import CliGlobalOptions from '../generated/cli-global-options.mdx'
## Anchor CLI Reference
This document provides a comprehensive reference for Anchor's command-line interface, including all available commands, subcommands, and options.
### Overview
The Anchor CLI has the following structure
```bash
anchor [OPTIONS]
```
Where `` is one of:
* `node` - Run an Anchor SSV Node
* `keysplit` - Split validator keys into shares
* `keygen` - Generate RSA keys for operator identification
#### Global Options
## Contributing to Anchor
[stable]: https://github.com/sigp/anchor/tree/stable
[unstable]: https://github.com/sigp/anchor/tree/unstable
Anchor welcomes contributions. If you are interested in contributing to to this project, and you want to learn Rust, feel free to join us building this project.
To start contributing,
1. Read our [how to contribute](https://github.com/sigp/anchor/blob/stable/CONTRIBUTING.md) document.
2. Setup a [development environment](./development_environment).
3. Browse through the [open issues](https://github.com/sigp/anchor/issues)
(tip: look for the [good first
issue](https://github.com/sigp/anchor/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
tag).
4. Comment on an issue before starting work.
5. Share your work via a pull-request.
### Branches
Anchor maintains two permanent branches:
* [`stable`][stable]: Always points to the latest stable release.
* This is ideal for most users.
* [`unstable`][unstable]: Used for development, contains the latest PRs.
* Developers should base their PRs on this branch.
### Rust
We adhere to Rust code conventions as outlined in the [**Rust
Styleguide**](https://doc.rust-lang.org/nightly/style-guide/).
Please use [clippy](https://github.com/rust-lang/rust-clippy) and
[rustfmt](https://github.com/rust-lang/rustfmt) to detect common mistakes and
inconsistent code formatting:
```bash
cargo clippy --all
cargo fmt --all --check
```
#### Panics
Generally, **panics should be avoided at all costs**. Anchor operates in an
adversarial environment (the Internet) and it's a severe vulnerability if
people on the Internet can cause Anchor to crash via a panic.
Always prefer returning a `Result` or `Option` over causing a panic. For
example, prefer `array.get(1)?` over `array[1]`.
If you know there won't be a panic but can't express that to the compiler,
use `.expect("Helpful message")` instead of `.unwrap()`. Always provide
detailed reasoning in a nearby comment when making assumptions about panics.
#### TODOs
All `TODO` statements should be accompanied by a GitHub issue.
```rust
pub fn my_function(&mut self, _something &[u8]) -> Result {
// TODO: something_here
// https://github.com/sigp/anchor/issues/XX
}
```
#### Comments
**General Comments**
* Prefer line (`//`) comments to block comments (`/* ... */`)
* Comments can appear on the line prior to the item or after a trailing space.
```rust
// Comment for this struct
struct Anchor {}
fn validate_attestation() {} // A comment on the same line after a space
```
**Doc Comments**
* The `///` is used to generate comments for Docs.
* The comments should come before attributes.
```rust
/// Stores the core configuration for this instance.
/// This struct is general, other components may implement more
/// specialized config structs.
#[derive(Clone)]
pub struct Config {
pub data_dir: PathBuf,
pub p2p_listen_port: u16,
}
```
#### Rust Resources
Rust is an extremely powerful, low-level programming language that provides
freedom and performance to create powerful projects. The [Rust
Book](https://doc.rust-lang.org/stable/book/) provides insight into the Rust
language and some of the coding style to follow (As well as acting as a great
introduction and tutorial for the language).
Rust has a steep learning curve, but there are many resources to help. We
suggest:
* [Rust Book](https://doc.rust-lang.org/stable/book/)
* [Rust by example](https://doc.rust-lang.org/stable/rust-by-example/)
* [Learning Rust With Entirely Too Many Linked Lists](http://cglab.ca/~abeinges/blah/too-many-lists/book/)
* [Rustlings](https://github.com/rustlings/rustlings)
* [Rust Exercism](https://exercism.io/tracks/rust)
* [Learn X in Y minutes - Rust](https://learnxinyminutes.com/docs/rust/)
## Development Environment
Most Anchor developers work on Linux or MacOS, however Windows should still
be suitable.
First, follow the [`Installation Guide`](./installation) to install
Anchor. This will install Anchor to your `PATH`, which is not
particularly useful for development but still a good way to ensure you have the
base dependencies.
The additional requirements for developers are:
* [`docker`](https://www.docker.com/). Some tests need docker installed and **running**.
### Using `make`
Commands to run the test suite are available via the `Makefile` in the
project root for the benefit of CI/CD. We list some of these commands below so
you can run them locally and avoid CI failures:
* `$ make cargo-fmt`: (fast) runs a Rust code formatting check.
* `$ make lint`: (fast) runs a Rust code linter.
* `$ make test`: (medium) runs unit tests across the whole project.
### Testing
As with most other Rust projects, Anchor uses `cargo test` for unit and
integration tests. For example, to test the `qbft` crate run:
```bash
cd anchor/common/qbft
cargo test
```
### Local Testnets
During development and testing it can be useful to start a small, local
testnet.
Testnet scripts will be built as the project develops.
## Frequently Asked Questions
* [What is sigp/anchor?](#sigp-anchor)
* [Who will use Anchor?](#who)
* [How do I know if Anchor is working normally?](#how-working)
* [Why is the validator not attesting?](#how-not-working)
* [Why Anchor has to connect to other peers?](#peers)
* [How to enable MEV on Anchor?](#mev)
* [How long does Anchor take to sync from a fresh sync?](#sync)
* [Why does Anchor need to generate key before start using it?](#anchor-key)
* [Anchor sync issue](#sync-issue)
### What is sigp/anchor?
Anchor is a Rust implementation of the Secret Shared Validator (SSV) protocol.
The SSV Network is a protocol built on Ethereum that allows a validator’s duties to be distributed among multiple operators (hence multiple nodes) in a trustless way.
In SSV, a set of operators collectively run an Ethereum validator by sharing the key (via cryptographic secret sharing) and coordinating actions via a consensus algorithm.
Anchor, as a SSV client, is run by node operators. Anchor communicates with other operators in a cluster to perform validator duties.
### Who will use Anchor?
Any individual that wants to become an operator in the SSV network will need to run a SSV client. Anchor is one of the SSV client that you can run.
### How do I know if Anchor is working normally?
A synced and working Anchor node has the following logs:
```
INFO Operator active operator_id=308 cluster_count=1
INFO Connected to beacon node(s) primary="http://localhost:5052/" total=1 available=1 synced=1
INFO All validators active current_epoch_proposers=0 active_validators=2 total_validators=2 epoch=53749 slot=1719971
INFO Network status subnets=1 peers=7 inbound=1 outbound=6 blocked_peers=0
INFO Processed contract events from block 1598180 log_count=0
```
When an attestation happens, Anchor will log:
```
INFO Successfully published attestations count=1 validator_indices=[1132617] head_block=0x528d08dda4afd11b7cd18028babe2f3de24b162636478104b776eaa8389a256d committee_index=0 slot=1719971 type="unaggregated"
```
The above logs indicate that Anchor is running normally.
### Why is the validator not attesting?
One reason the validator that you operate for is not performing its duties could be due to some operators being offline. For example, in a cluster of 4 operators, at most 1 operator can be offline.
If 2 operators are offline at the same time, then the validator will not be able to perform its duties. If your Anchor is running normally, it will log:
```
WARN Signing selection proof timed out - other operators might be offline
ERROR Failed to produce duty and proof error=FailedToProduceSelectionProof(SpecificError(Timeout)) msg="may impair attestation duties"
WARN No attestations were published
```
Check that Anchor is connected to [sufficient peers](./faq#peers). If Anchor is connected to sufficient peers, then once other operators came back online, the validator should resume attesting.
Another reason is the Anchor on your node is not ready. It could be due to the beacon node or the execution client is still syncing. If the beacon node is syncing, you will see the log:
```
ERROR No synced beacon nodes total=1 available=1 synced=0
ERROR Failed to update slot metadata err="Some endpoints failed, num_failed: 2 http://localhost:5052/ => RequestFailed(\"Failed to produce attestation data: ServerMessage(ErrorMessage { code: 503, message: \\\"SERVICE_UNAVAILABLE: beacon node is syncing: head slot is 1720767, current slot is 1720858\\\", stacktraces: [] })\"), http://localhost:5052/ => RequestFailed(\"Failed to produce attestation data: ServerMessage(ErrorMessage { code: 503, message: \\\"SERVICE_UNAVAILABLE: beacon node is syncing: head slot is 1720767, current slot is 1720858\\\", stacktraces: [] })\")"
```
If the execution client is syncing, Anchor logs:
```
WARN Waiting for EL to finish syncing
```
The error or warning Logs will go away once the beacon node and the execution client are synced.
### Why Anchor has to connect to other peers?
The peers that Anchor connects to are not beacon nodes. In Anchor, it connects to other SSV peers (other Anchor/go-ssv nodes).
If an operator operates for a cluster, then Anchor will subscribe to 1 subnet.
It will connect to peers in the same subnet that are in the same committee so that it can perform validator duties. This is why Anchor needs to connect to other peers.
In practice, an operator may run validators for two or more clusters. In this case, Anchor will subscribe to more subnets and connect to more peers.
The following log shows how many peers Anchor is connected to:
```
INFO Network status subnets=1 peers=5 inbound=1 outbound=4 blocked_peers=0
```
Anchor dynamically adjusts the target peer count based on the subnets subscribed. However, if you frequently see the log:
```
WARN Round timer elapsed
```
you may consider increasing the peer count using `--target-peers`.
### How to enable MEV on Anchor?
To enable MEV on Anchor, use the flag `--builder-proposals`.
If the node is selected to be a leader during a proposal, it will request for mev blocks. If the node is not a leader during a proposal, Anchor will sign MEV blocks proposed by other nodes regardless of whether this flag is set.
### How long does Anchor take to sync from a fresh sync?
Anchor takes a few minutes on Hoodi, and about 15 minutes on mainnet to sync. When Anchor is syncing, it will log:
```
INFO Syncing
INFO Historical sync in progress processing_block=501064
INFO Operator present on chain, waiting for sync operator_id=307
INFO Processed all events up to block 1599236
INFO Starting live sync
INFO Sync complete, starting services...
```
Once you see `Sync complete`, then Anchor is ready.
### Why does Anchor need to generate key before start using it?
It is true that Anchor does not hold any validator keys (not even the partial split key), which raises the question above.
The key that Anchor generates is a public-private key pair. The operator will use the public key to register as an operator in the SSV network.
Anchor then uses the private key to decrypt and to be identified as a valid operator on the SSV network (that corresponds to the public key registered).
The private key is a proof that an operator holds the key that corresponds to the public key that is used to register to be an operator in the SSV network.
This is why safe keeping of the private key is important, as losing the private key implies that the operator can no longer operate on the SSV network.
### Anchor sync issue
If Anchor shows:
```
INFO Synced, waiting for operator key to appear on chain
```
but you have already registered as an operator on the SSV network, one reason could be due to using Reth as the execution client.
This is because Reth currently prunes receipts of smart contracts by default. This prevents Anchor (or any SSV node) from syncing.
If you are using Reth as the execution client, you will need to run as [full node](https://reth.rs/run/faq/pruning/) or switch to another execution client.
A resync may be required by deleting the Anchor database `anchor_db.sqlite` in the database directory.
## SSV NodeInfo Handshake Protocol Specification
This document specifies the **SSV NodeInfo Handshake Protocol**. The protocol is used by SSV-based nodes to exchange basic node metadata and validate each other's identity when establishing a connection over Libp2p under a dedicated protocol ID.
***
### Table of Contents
* [1. Introduction](#1-introduction)
* [2. Definitions](#2-definitions)
* [2.1 Terminology](#21-terminology)
* [2.2 Domain Separation](#22-domain-separation)
* [3. Protocol Constants](#3-protocol-constants)
* [4. Data Structures](#4-data-structures)
* [4.1 Envelope](#41-envelope)
* [4.2 NodeInfo](#42-nodeinfo)
* [4.3 NodeMetadata](#43-nodemetadata)
* [5. Serialization and Signing](#5-serialization-and-signing)
* [5.1 Envelope Fields](#51-envelope-fields)
* [5.2 NodeInfo JSON Layout](#52-nodeinfo-json-layout)
* [5.3 Signature Preparation](#53-signature-preparation)
* [6. Handshake Protocol Flows](#6-handshake-protocol-flows)
* [6.1 Protocol ID](#61-protocol-id)
* [6.2 Request Phase](#62-request-phase)
* [6.3 Response Phase](#63-response-phase)
* [6.4 Network Mismatch Checks](#64-network-mismatch-checks)
* [7. Security Considerations](#7-security-considerations)
* [8. Rationale and Notes](#8-rationale-and-notes)
* [9. Examples](#9-examples)
***
### 1. Introduction
The SSV NodeInfo Handshake Protocol defines how two SSV nodes exchange, sign, and verify each other's **NodeInfo**, which includes a `network_id` (such as "holesky", "prater", etc.) and optional metadata about node software versions or subnets. The protocol uses a request-response style handshake over Libp2p under a dedicated protocol ID.
The high-level handshake steps are:
1. **Requester** sends an Envelope (containing its NodeInfo) to the peer.
2. **Responder** verifies this Envelope, checks the `network_id`, and replies with its own Envelope.
3. **Requester** verifies the responder's Envelope.
4. Both sides proceed if verification succeeds; otherwise, the handshake is considered failed.
***
### 2. Definitions
#### 2.1 Terminology
| **Term** | **Definition** |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Envelope** | A Protobuf-encoded message containing a `public_key`, `payload_type`, `payload`, and `signature` (covering a domain-separated concatenation of fields). |
| **NodeInfo** | A JSON-based structure holding key node attributes like `network_id` plus optional metadata. |
| **Handshake** | The request-response exchange of Envelopes between two nodes at connection time. |
#### 2.2 Domain Separation
* **Domain**: `"ssv"`.\
Used to separate signatures for different contexts or protocols.
***
### 3. Protocol Constants
| **Name** | **Value** | **Description** |
| -------------- | ----------------- | ---------------------------------------------------- |
| `DOMAIN` | `ssv` | Fixed ASCII text used during signature generation. |
| `PAYLOAD_TYPE` | `ssv/nodeinfo` | Identifies the payload as an SSV NodeInfo structure. |
| `PROTOCOL_ID` | `/ssv/info/0.0.1` | Libp2p protocol ID used for the handshake. |
***
### 4. Data Structures
#### 4.1 Envelope
The Envelope is a Protobuf message:
```protobuf
message Envelope {
bytes public_key = 1;
bytes payload_type = 2;
bytes payload = 3;
bytes signature = 5;
}
```
#### 4.2 NodeInfo
```text
NodeInfo:
- network_id: String
- metadata: NodeMetadata (optional)
```
#### 4.3 NodeMetadata
```text
NodeMetadata:
- node_version: String
- execution_node: String
- consensus_node: String
- subnets: String
```
***
### 5. Serialization and Signing
#### 5.1 Envelope Fields
1. **public\_key**
* Sender’s public key in serialized form (e.g., compressed Secp256k1 or raw Ed25519 bytes).
* The public key is encoded and decoded using Protobuf.
* For reference, Libp2p has a [Peer Ids and Keys](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md), which may be consulted for consistent handling across implementations.
2. **payload\_type**
* MUST be `"ssv/nodeinfo"` in this protocol.
* Used to identify how to interpret `payload`.
3. **payload**
* Contains `NodeInfo` data in JSON (described below).
4. **signature**
* A cryptographic signature covering `DOMAIN || payload_type || payload`.
#### 5.2 NodeInfo JSON Layout
Internally, the protocol uses a “legacy” layout for `NodeInfo` serialization, with a top-level JSON structure:
```json
{
"Entries": [
"", // (Index 0) Old forkVersion, not used
"", // (Index 1) The NodeInfo.network_id
"" // (Index 2) if NodeMetadata is present
]
}
```
* If the array has fewer than 2 entries, the payload is invalid.
* If the array has 3 entries, the 3rd entry is a JSON object for metadata, for example:
```json
{
"NodeVersion": "...",
"ExecutionNode": "...",
"ConsensusNode": "...",
"Subnets": "..."
}
```
#### 5.3 Signature Preparation
To **sign** an Envelope, implementations:
1. Construct the unsigned message:
```text
unsigned_message = DOMAIN || payload_type || payload
```
2. Sign `unsigned_message` using the node’s private key.
3. Write the resulting signature to `signature`.
To **verify** an Envelope:
1. Recompute the `unsigned_message`.
2. Verify using `public_key` against `signature`.
If verification fails, the handshake **MUST** abort.
***
### 6. Handshake Protocol Flows
#### 6.1 Protocol ID
Both peers must speak the protocol identified by:
```text
/ssv/info/0.0.1
```
#### 6.2 Request Phase
1. **Build Envelope**
* The initiating node (Requester) serializes its `NodeInfo` into JSON (the `payload`).
* Sets `payload_type = "ssv/nodeinfo"`.
* Prepends `DOMAIN = "ssv"` when computing the signature.
* Places the resulting `public_key` and `signature` into the Envelope.
2. **Send Request**
* The requester sends this Envelope as the request.
3. **Wait for Response**
* The requester awaits the single response from the Responder.
#### 6.3 Response Phase
1. **Receive & Verify**
* The responder verifies the incoming Envelope:
* Check signature correctness.
* Extract `NodeInfo`.
* Validate `network_id` if necessary (see [6.4](#64-network-mismatch-checks)).
2. **Build Response**
* If valid, the responder builds and signs its own Envelope containing its `NodeInfo`.
3. **Send Response**
* The responder sends the Envelope back to the requester.
4. **Requester Verifies**
* The requester verifies the signature, parses `NodeInfo`, and checks `network_id`.
#### 6.4 Network Mismatch Checks
* Implementations **MUST** check whether the received `NodeInfo`’s `network_id` matches their local `network_id`.
* If they mismatch, the implementation **SHOULD** reject the connection.
***
### 7. Security Considerations
* **Signature Validation** is mandatory. Any failure to verify the Envelope’s signature indicates an invalid handshake.
* **Public Key Authenticity**: The Envelope’s `public_key` is not implicitly trusted. It must match the verified signature.
* **Network Mismatch**: Avoid bridging distinct SSV or Ethereum networks. Peers claiming the wrong `network_id` should be rejected.
* **Payload Size**: Although `NodeInfo` is generally small, implementations **SHOULD** impose a maximum bound for payload. Any request or response exceeding this size limit **SHOULD** be rejected.
***
### 8. Rationale and Notes
* Using a Protobuf-based Envelope simplifies cross-language interoperability.
* The domain separation string (`"ssv"`) prevents signature reuse in other contexts.
* The “legacy” `Entries` layout ensures backward-compatibility with older SSV implementations.
***
### 9. Examples
#### 9.1 Example Envelope in Hex
An example Envelope could be hex-encoded as:
```text
0a250802122102ba6a707dcec6c60ba2793d52123d34b22556964fc798d4aa88ffc41a00e42407120c7373762f6e6f6465696e666f1aa5017b22456e7472696573223a5b22222c22686f6c65736b79222c227b5c224e6f646556657273696f6e5c223a5c22676574682f785c222c5c22457865637574696f6e4e6f64655c223a5c22676574682f785c222c5c22436f6e73656e7375734e6f64655c223a5c22707279736d2f785c222c5c225375626e6574735c223a5c2230303030303030303030303030303030303030303030303030303030303030303030305c227d225d7d2a473045022100b8a2a668113330369e74b86ec818a87009e2a351f7ee4c0e431e1f659dd1bc3f02202b1ebf418efa7fb0541f77703bea8563234a1b70b8391d43daa40b6e7c3fcc84
```
Decoding reveals (high-level view):
```text
Envelope {
public_key = ,
payload_type = "ssv/nodeinfo",
payload = {
"Entries": [
"",
"holesky",
"{\"NodeVersion\":\"geth/x\",\"ExecutionNode\":\"geth/x\",\"ConsensusNode\":\"prysm/x\",\"Subnets\":\"00000000000000000000000000000000\"}"
]
},
signature =
}
```
#### 9.2 Verifying the Envelope
1. Recompute: `domain = "ssv"`
```text
unsigned_message = "ssv" || "ssv/nodeinfo" || payload_bytes
```
2. Verify signature with `public_key`.
3. Parse payload JSON => parse `NodeInfo` => check `network_id`.
## Anchor Installation Guide
This guide provides step-by-step instructions for installing Anchor.
### Recommended System Requirements
Anchor itself is a light-weight client when run alone.
However, as Anchor needs to connect to a beacon node and an execution client, this implies that a complete setup will require
the hardware specifications equivalent to running an Ethereum node.
| Hardware | Mainnet | Hoodi testnet |
| -------- | ------------------------------------------ | ------------------------------------------ |
| CPU | AMD Ryzen, Intel Broadwell, ARMv8 or newer | AMD Ryzen, Intel Broadwell, ARMv8 or newer |
| Memory | 32 GB RAM | 16 GB RAM |
| Storage | 2 TB | 200 GB |
Anchor can be run independently of an Ethereum beacon node and execution client, in which case the
requirements are that of a standard validator client. These are very light weight and can run on
small machines such as raspberry pi's. However, Anchor does have its own network and has minimum
bandwidth requirements of around 20MB/s.
### 1. Download the Latest Release from GitHub
1. Visit the [Anchor Releases page](https://github.com/sigp/anchor/releases).
2. Download the appropriate binary for your operating system.
3. Extract the file if necessary and move the binary to a location in your `PATH` (e.g., `/usr/local/bin/`).
#### Example
```bash
# General download link format
# Replace and with the appropriate values.
wget https://github.com/sigp/anchor/releases/download//anchor--.tar.gz
tar -xvf anchor--.tar.gz
# Specific version example
wget https://github.com/sigp/anchor/releases/download/v1.3.0/anchor-v1.3.0-gnu.tar.gz
tar -xvf anchor-v1.3.0-gnu.tar.gz
sudo mv anchor /usr/local/bin/
```
Verify the installation:
```bash
anchor --version
```
***
### 2. Run Anchor Using Docker
1. Pull the latest Anchor Docker image:
```bash
docker pull sigp/anchor:latest
```
2. Verify the installation:
```bash
docker run --rm -it sigp/anchor:latest --version
```
### 3. Clone and Build Locally
1. Clone the Anchor repository and build the binary:
```bash
git clone https://github.com/sigp/anchor.git
cd anchor
make
```
The binary should be installed at `~/.cargo/bin`.
2. If `~/.cargo/bin` is not in your `PATH` add it:
```bash
export PATH=$HOME/.cargo/bin:$PATH
```
3. Verify the installation:
```bash
anchor --version
```
## Welcome to Anchor
**Anchor** is a high-performance Secret Shared Validators (SSV) client written in Rust, designed to enable distributed validator technology for Ethereum staking.
### SSV Overview
Secret Shared Validators (SSV) is a protocol that splits a validator key into multiple shares, distributed across different operators.
Instead of running a validator on a single machine, SSV splits the validator's duties across multiple operators, creating a more resilient and decentralized staking infrastructure. This approach provides:
* **Fault Tolerance**: Your validator remains online even if some operators go offline
* **No Single Point of Failure**: No single operator can compromise your validator
* **Slashing Protection**: Enhanced protection against slashing events
* **Decentralization**: Distribute trust across multiple independent operators
### Why Anchor?
Written in Rust, Anchor focuses on memory safety and security. As an independent implementation of the SSV protocol,
Anchor promotes client diversity in the SSV space, reducing the risk of network-wide failures. A
diverse set of client operators will significantly improve the resilience of the SSV network.
Anchor is specifically optimized for efficiency, fast consensus and networking implementation.
### Contact
The best place to reach us in the
[#anchor](https://discord.com/channels/605577013327167508/1376460624069918720) channel in our [Lighthouse
discord server](https://discord.gg/cyAszAh).
For security related matters, please reach out to
[security@sigmaprime.io](mailto\:security@sigmaprime.io) and encrypt sensitive
messages with our [PGP key](https://keybase.io/sigp/pgp_keys.asc?fingerprint=15e66d941f697e28f49381f426416dc3f30674b0).
### About This Documentation
This documentation is open source and community-driven. We welcome [contributions](https://github.com/sigp/anchor/tree/unstable/docs) to help improve the experience for all users.
***
:::tip
Need Help?
* Check our [FAQ](/faq) for common questions
* Join the community on [Discord](https://discord.com/invite/cyAszAh)
* Report issues or request features in our [issue tracker](https://github.com/sigp/anchor/issues)
:::
## Metrics
Anchor comes pre-built with a suite of metrics for developers or users to monitor the health
and performance of the node.
They must be enabled at runtime using the `--metrics` CLI flag.
### Usage
In order to run a metrics server, `docker` is required to be installed.
Once docker is installed, a metrics server can be run locally via the following steps:
1. Start an anchor node with `$ anchor --metrics`
* The `--metrics` flag is required for metrics.
2. Move into the metrics directory `$ cd metrics`.
3. Bring the environment up with `$ docker-compose up --build -d`.
4. Ensure that Prometheus can access your Anchor node by ensuring it is in
the `UP` state at [http://localhost:9090/targets](http://localhost:9090/targets).
5. Browse to [http://localhost:3000](http://localhost:3000)
* Username: `admin`
* Password: `changeme`
6. Import some dashboards from the `metrics/dashboards` directory in this repo:
* In the Grafana UI, go to `Dashboards` -> `Manage` -> `Import` -> `Upload .json file`.
* The `anchor-dash.json` dashboard is a good place to start.
### Dashboards
A suite of dashboards can be found in `metrics/dashboard` directory. The Anchor team will
frequently update these dashboards as new metrics are introduced.
We welcome Pull Requests for any users wishing to add their dashboards to this repository for
others to share.
### Scrape Targets
Prometheus periodically reads the `metrics/scrape-targets/scrape-targets.json` file. This
file tells Prometheus which endpoints to collect data from. The current file is setup to read
from Anchor on its default metrics port. You can add additional endpoints if you want to collect
metrics from other servers.
An example is Lighthouse. You can collect metrics from Anchor and Lighthouse simultaneously if
they are both running. We have an example file `scrape-targets-lighthouse.json` which allows this.
You can replace the `scrape-targets.json` file with the contents of
`scrape-targets-lighthouse.json` if you wish to collect metrics from Anchor and Lighthouse
simultaneously.
### Hosting Publicly
By default Prometheus and Grafana will only bind to localhost (127.0.0.1), in
order to protect you from accidentally exposing them to the public internet. If
you would like to change this you must edit the `http_addr` in `metrics/grafana/grafana.ini`.
## Migrating to Anchor
Anchor is designed to be interoperable with the go-ssv's key formats. This means the process of migrating from go-ssv to Anchor is fairly straight forward. We simply need to move the operator key from the go-ssv directory into the Anchor data directory.
### Precautions
There are a host of issues that can arise if two identical operators are running at the same time. It is important to stop the go-ssv node before running the Anchor node. For safety, we strongly recommend **moving** the keys rather than copying them, to prevent multiple instances of the same operator id running at the same time.
### Moving to Anchor
Firstly, ensure Anchor is installed and working by following the steps in [Installation](/installation). To check, ensure the expected version of Anchor can be run:
```bash
anchor --version
```
The next step to move the operator key from the go-ssv node to Anchor. By default, go-ssv stores the operator encrypted private key at:
```
~/ssv-stack/ssv-node-data/encrypted_private_key.json
```
By default, Anchor expects the operator key in the path:
```
~/.anchor//encrypted_private_key.json
```
Here `` represents the network the operator is going to run on, for example `hoodi` or `mainnet`.
To migrate from go-ssv to Anchor, we simply need to move the operator private key. If using the default values, for mainnet, this would look like:
```bash
mv ~/ssv-stack/ssv-node-data/encrypted_private_key.json ~/.anchor/mainnet/encrypted_private_key.json
```
Once this key is moved, Anchor should be able to be run. See the [Running an Operator](/running_an_operator) for how to fully run the Anchor node as an operator.
You will need to put the password that decrypts the operator key into a file and reference it in Anchor's CLI via the `--password-file ` parameter.
:::warning
If Anchor fails to start with the message `Unable to parse key: unknown field pubkey, expected one of pubKey (...)`, please change "pubkey" to "pubKey" in the encrypted\_private\_key.json file. This is a known issue and will be fixed in a future release.
:::
## For Protocol Developers
*Documentation for protocol developers.*
This section lists Anchor-specific decisions that are not strictly spec'd and may be useful for
other protocol developers wishing to interact with Anchor.
* [SSV Handshake Protocol](/handshake)
## Running a Validator on SSV
Validators can be created to run on the SSV network or can be migrated from a currently running
validator client to an SSV cluster. This section provides a simple guide of the process of
running a validator on the SSV network.
### New Validator
As described in the [SSV New Validator Documentation](https://docs.ssv.network/stakers/validator-management/creating-a-new-validator/), the general procedure is a two step process:
1. Generate validator keys and deposit ETH (minimum 32 ETH for a validator)
2. Register the validator to SSV and split the validator key
The first step follows the standard process for creating an Ethereum validator. The general approach is to use the [ethstaker-deposit-cli](https://github.com/ethstaker/ethstaker-deposit-cli) to generate keys and create a deposit json, then use the [Staking Launchpad](https://launchpad.ethereum.org/en/) to deposit the Ethereum.
The second step is the same process as migrating a validator and is covered in the next section.
### Migrating a Validator
The steps involved in migrating a running validator are covered in the [Migrating a validator](https://docs.ssv.network/stakers/validator-management/distributing-a-validator) SSV documentation.
The procedure involves connecting to the [SSV Webapp](https://app.ssv.network/) and choosing a set of operators that you would like to run your validator. This will form an SSV cluster. Once a set of operators are chosen you then need to split your validator's private key into shards specific to each operator.
SSV offers a distributed key splitting mechanism which is outlined in their documentation. Anchor offers key splitting functionality also, which can be done via the [anchor keysplit](/cli-keysplit) command.
## Running an Anchor SSV Operator
### What is an SSV Operator
An SSV operator is a node that holds shares of validators' keys and participates in committees to perform Ethereum validation duties. The SSV network enables distributed validation where multiple operators collectively validate without any single operator having access to the complete validator key.
If you want to migrate an existing key from the Golang implementation of SSV, you can directly proceed to Step 3 or check out the dedicated [migrating to anchor](/migrate_to_anchor) section.
### Client Dependencies
Anchor requires access to an Ethereum Consensus Beacon Node (via the HTTP API) and to an execution
client via **BOTH** the HTTP API and Websockets (WS) protocols.
#### Consensus Clients
All consensus clients have the required http API. Generally they need to be enabled via the CLI. For
example with Lighthouse, the client needs to be run with the `--http` flag:
```bash
lighthouse bn ... --http --http-address 0.0.0.0
```
The `http-address` may be required if Anchor is accessing the beacon node from another computer.
#### Execution Clients
For Anchor to function with an execution client, it requires access to both the HTTP API and its
websockets API. These can generally be enabled similarly to beacon nodes via CLI flags. For example,
for go-ethereum, the following flags can enable these:
```bash
geth ... --http --http.addr "0.0.0.0" --http.corsdomain "*" --ws --ws.origins "*" --ws.addr
"0.0.0.0"
```
It is important to note that only the `--http` and `--ws` flags are necessary. The other flags are
added here as they may help when connecting to the node remotely. These have important security
ramifications and should only be set if needed. Check each clients documentation to ensure which
flags are needed for your specific setup.
:::warning
The `reth` execution client currently prunes receipts of smart contracts by default. This prevents
Anchor (or any SSV node) from syncing. In order to run a `reth` node with Anchor, `reth` should be
prevented from [pruning](https://reth.rs/run/faq/pruning/) receipts, e.g. by running it as an archive node.
:::
### Setting up Anchor
**Step 1: Generate RSA keys**
Anchor includes a key generation tool to create the RSA keys needed for operator identity:
```bash
# Generate unencrypted keys (for development)
anchor keygen
# Generate encrypted keys (recommended for production)
anchor keygen --encrypt --data-dir /path/to/keys/directory
```
This will generate:
* Your private key. If you choose to not encrypt your key, the file will be called `private_key.txt`. For encrypted keys, the file will be called `encrypted_private_key.json`.
* The public key output in the console and a file called `public_key.txt`.
Save your public key as you'll need it for on-chain registration. **Back up your key as it cannot be restored if lost!**
**Step 2: Register as an Operator on the SSV Network**
To register an operator, follow the instructions in the official
[ssv docs](https://docs.ssv.network/operators/operator-management/registration).
**Step 3: Configure and run your Anchor node**
Create a directory for Anchor-related data and move the generated private key into the directory. By default, Anchor
uses `~/.anchor/`, where `` is `mainnet` or `hoodi`. We use `hoodi` below:
```bash
mkdir -p ~/.anchor/hoodi
mv encrypted_private_key.json ~/.anchor/hoodi/
```
:::info
If switching from the go SSV node, simply move the `encrypted_private_key.json` file from the default
`~/ssv-stack/ssv-node-data/` directory to `~/.anchor//` directory.
:::
Use the [CLI Reference](./cli) or `--help` to launch the node. If you use an encrypted key, you must specify the password via a password file or interactively input it when starting the node.
Mainnet:
```bash
anchor node \
--datadir ~/.anchor/mainnet \
--beacon-nodes http://localhost:5052 \
--execution-rpc http://localhost:8545 \
--execution-ws ws://localhost:8546 \
--password-file /path/to/file
```
Hoodi testnet:
```bash
anchor node \
--network hoodi \
--datadir ~/.anchor/hoodi \
--beacon-nodes http://localhost:5052 \
--execution-rpc http://localhost:8545 \
--execution-ws ws://localhost:8546 \
--password-file /path/to/file
```
All options used in this example (except for the `password-file`) are actually used with the default values and can therefore be omitted, or adjusted to your setup.
:::info
A beacon node HTTP API, execution client HTTP API and execution client WS API are required for
Anchor to function. If they are not running locally on the default ports, they must be
specified in the CLI
:::
The Anchor node will use the same ports as used by Go-SSV unless explicitly overridden. See [Advanced Networking](./advanced_networking) for more information