# 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