Anchor Book

Documentation for Anchor users and developers.

The Anchor client is an SSV client written in rust. Anchor has been built from the ground up to be highly performant and secure.

This book aims to provide help and support to users and developers of this client.

Note: The Anchor client is currently under active development and should not be used in a production setting.

About this Book

This book is open source, contribute at github.com/sigp/anchor/book.

The Anchor CI system maintains a hosted version of the unstable branch at anchor-book.sigmaprime.io.

Metrics

Anchor comes pre-built with a suite of metrics for developers or users to monitor the health and performance of their 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.
  5. Browse to 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 Summary.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.

Frequently Asked Questions

What is sigp/anchor

The rust implementation of the Secret Shared Validator (SSV) protocol.

Development Environment

Most Anchor developers work on Linux or MacOS, however Windows should still be suitable.

First, follow the Installation Guide 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. 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.
  • $ make test-specs: (medium) runs the Anchor test vectors.
  • $ make test-full: (slow) runs the full test suite (including all previous commands). This is approximately everything that is required to pass CI.

Testing

As with most other Rust projects, Anchor uses cargo test for unit and integration tests. For example, to test the qbft crate run:

cd src/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.

Contributing to Anchor

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 document.
  2. Setup a development environment.
  3. Browse through the open issues (tip: look for the good first issue tag).
  4. Comment on an issue before starting work.
  5. Share your work via a pull-request.

Branches

Anchor maintains two permanent branches:

  • stable: Always points to the latest stable release.
    • This is ideal for most users.
  • 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.

Please use clippy and rustfmt to detect common mistakes and inconsistent code formatting:

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.

#![allow(unused)]
fn main() {
pub fn my_function(&mut self, _something &[u8]) -> Result<String, Error> {
  // 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.
#![allow(unused)]
fn main() {
// 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.
#![allow(unused)]
fn main() {
/// 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 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:

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.

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.

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:

graph LR

  A(Core Client) <--> B(Duties Service)
  B <--> C(Processor)
  C <--> D(Network)
  C <--> E(QBFT)