Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation #496

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
189 changes: 25 additions & 164 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,182 +5,43 @@

Secret Shared Validator ('SSV') is a unique technology that enables the distributed control and operation of an Ethereum validator.

SSV uses an MPC threshold scheme with a consensus layer on top ([Istanbul BFT](https://arxiv.org/pdf/2002.03613.pdf)),
that governs the network. \
Its core strength is in its robustness and fault tolerance which leads the way for an open network of staking operators
SSV uses an MPC threshold scheme with a consensus layer on top ([Istanbul BFT](https://arxiv.org/pdf/2002.03613.pdf)) to execute Ethereum duties in a secure way.

Its core strength is in its robustness and fault tolerance which leads the way for an open network of staking operators
to run validators in a decentralized and trustless way.

## SSV Spec
This repo contains the spec for SSV.Network node.

### SSVMessage
SSV network message is called SSVMessage, it includes a MessageID and MsgType to route messages within the SSV node code, and, data for the actual message (QBFT/ pre/ Post consensus messages for example).

Any message data struct must be signed and nested within a signed message struct which follows the MessageSignature interface.
A signed message structure includes the signature over the data structure, the signed root and signer list.

#### QBFT Message
This type of message is used for all consensus messages
```go
type Message struct {
MsgType MessageType
Height Height // QBFT instance Height
Round Round // QBFT round for which the msg is for
Identifier []byte // instance Identifier this msg belongs to
Data []byte
}
```
This repo contains the spec of the SSV protocol for the SSV.Network node.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the the SSV.Network node, perhaps just the SSV node (or the SSV network node) ?

Update: I see, it's called like that through the docs ... a redundant term to be honest, I'd change it to SSV network node if that's easy to do.


#### Partial Signature Message
Used for pre and post consensus sigantures for collecting partial BN signatures and then reconstructing them
```go
type PartialSignatureMessage struct {
Type PartialSigMsgType
PartialSignature []byte // The beacon chain partial Signature for a duty
SigningRoot []byte // the root signed in PartialSignature
Signers []types.OperatorID
}
```
## The Network

### Signing messages
The KeyManager interface has a function to sign roots, a slice of bytes.
The root is computed over the original data structure (which follows the MessageRoot interface), domain and signature type.

**Use ComputeSigningRoot and ComputeSignatureDomain functions for signing**
```go
func ComputeSigningRoot(data MessageRoot, domain SignatureDomain) ([]byte, error) {
dataRoot, err := data.GetRoot()
if err != nil {
return nil, errors.Wrap(err, "could not get root from MessageRoot")
}

ret := sha256.Sum256(append(dataRoot[:], domain...))
return ret[:], nil
}
```
```go
func ComputeSignatureDomain(domain DomainType, sigType SignatureType) SignatureDomain {
return SignatureDomain(append(domain, sigType...))
}
```
The SSV network is composed of several `operators`. Each operator is identified by a unique [`OperatorID`](./types/docs/README.md#operatorid-and-committeeid) and holds a netwok key for message authentication.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

several -> many

holds a netwok key for message authentication. -> holds a network key for message authentication on SSV network.


Domain Constants:

| Domain | Value | Description |
|----------------|-------------------------------|-----------------------------------|
| Primus Testnet | DomainType ("primus_testnet") | Domain for the the Primus testnet |

Signature type Constants:

| Signature Type | Value | Description |
|----------------------|----------------------|------------------------------------------|
| QBFTSignatureType | [] byte {1, 0, 0, 0} | SignedMessage specific signatures |
| PartialSignatureType | [] byte {2, 0, 0, 0} | PostConsensusMessage specific signatures |
| DKGSignatureType | [] byte {3, 0, 0, 0} | PostConsensusMessage specific signatures |

## Validator and Runners
A validator instance is created for each validator independently, each validator will have multiple Runner for each beacon chain duty type (Attestations, Blocks, etc.)
Duty runners are responsible for processing incoming messages and act upon them, completing a full beacon chain duty cycle.

Each duty starts by calling the StartNewDuty func in the respective Runner.
StartNewDuty might return error if can't start a new duty, depending on the previous duty life cycle.
As a general rule, when a runner is executing a duty in the consensus phase, a new duty can't start.
Pre/ Post partial signature collection will not enable starting a new duty if not completed except if timed out.

**Attestation Duty Full Cycle:**\
-> Wait to slot 1/3\
   -> Received new beacon chain duty\
      -> Check can start a new consensus instance\
         -> Come to consensus on duty + attestation data\
            -> Broadcast and collect partial signature to reconstruct signature\
               -> Reconstruct signature, broadcast to BN

**Block Proposal Duty Full Cycle:**\
-> Received new beacon chain duty\
   -> Check can start a new consensus instance\
      -> Sign partial RANDAO and wait for other signatures\
         -> Come to consensus on duty + beacon block\
            -> Broadcast and collect partial signature to reconstruct signature\
               -> Reconstruct signature, broadcast to BN

**Attestation Aggregator Duty Full Cycle:**\
-> Received new beacon chain duty\
   -> Check can start a new consensus instance\
      -> Sign partial selection proof and wait for other signatures\
         -> Wait to slot 2/3\
            -> Come to consensus on duty + aggregated selection proof\
               -> Broadcast and collect partial signature to reconstruct signature\
                  -> Reconstruct signature, broadcast to BN

**Sync Committee Duty Full Cycle:**\
-> Wait to slot 1/3\
   -> Received new beacon chain duty\
      -> Check can start a new consensus instance\
         -> Come to consensus on duty + sync message\
            -> Broadcast and collect partial signature to reconstruct signature\
               -> Reconstruct signature, broadcast to BN

**Sync Committee Aggregator Duty Full Cycle:**\
-> Received new beacon chain duty\
   -> Check can start a new consensus instance\
      -> Locally get sync subcommittee indexes for slot\
         -> Partial sign contribution proofs (for each subcommittee) and wait for other signatures\
            -> wait to slot 2/3\
               -> Come to consensus on duty + contribution (for each subcommittee)\
                  -> Broadcast and collect partial signature to reconstruct signature\
                     -> Reconstruct signature, broadcast to BN

A runner holds a QBFT controller for processing QBFT messages and a State which keeps progress for all stages of duty execution: pre/ post consensus messages.
Partial signatures are collected and reconstructed (when threshold reached) to be broadcasted to the BN network.

## Validator Share
A share is generated and broadcasted publicly when a new SSV validator is registered to its operators.
Shares include:
- Node ID: The Operator ID the share belongs to
- Validator Public Key
- Committee: An array of Nodes that constitute the SSV validator committee. A node must include it's NodeID and share public key.
- Domain

```go
type Share struct {
OperatorID OperatorID
ValidatorPubKey ValidatorPK
SharePubKey []byte
Committee []*Operator
Quorum, PartialQuorum uint64
DomainType DomainType
Graffiti []byte
}
```
Every `validator` is registered to a unique committee of operators to execute its duties. When this happens, [`Share`](./types/docs/README.md#share--sharemember-and-committeemember--operator) objext are broadcasted publicly to the network.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objext -> objects

are broadcasted publicly

perhaps worth mentioning what "broadcasted publicly" means, is it through writing those shared into Ethereum smart contract or just sending messages to be delivered through SSV network


## Node
A node represents a registered SSV operator, each node has a unique ID and encryption key which is used to encrypt assigned shares.
NodeIDs are extremely important as they are used when splitting a validator key via Shamir-Secret-Sharing, later on they are used to verify messages and reconstruct signatures.
A single `committee` of operators may run several validators at the same time.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run several validators at the same time -> run several validators (of independent users) at the same time


Shares use the Node data (for committee) to verify that incoming messages were signed by a committee member
The following image illustrates these relations.

```go
type Node struct {
NodeID NodeID
PubKey []byte
}
```
<p align="center",float="left">
<img src="./ssv/docs/network.drawio.png", width="45%" height="10%">
</p>

To execute the validators' duties, the operators exchange messages in the network. These messages are of type [`SignedSSVMessage`](./types/README.md#signedssvmessage-and-ssvmessage) which wraps either a consensus message ([`qbft.Message`](qbft/docs/README.md#message)) or a partial signature message ([`PartialSignatureMessages`](./types/README.md#partialsignaturemessages)).

To understand the protocol for executing duties, check out the [protocol explanation file](./ssv/README.md). The protocol is built upon the QBFT BFT consensus protocol. To understand our specification for the QBFT protocol, check the [QBFT module file](./qbft/README.md).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To understand the protocol for executing duties, check out the protocol explanation file. The protocol is built upon the QBFT BFT consensus protocol. To understand our specification for the QBFT protocol, check the QBFT module file.

->

To understand the protocol for executing duties check out protocol overview doc. Note, it's built around QBFT BFT consensus protocol.


### NodeID and share creation example
NodeID is unique to each node, starting from ID 1 and incrementally going froward.
Each ValidatorPK has a committee of nodes, each with a unique ID, share and share public key.
Also, for more details, check the documentation for the [`Operator`](./types/docs/README.md#share--sharemember-and-committeemember--operator), [`Validator`](./ssv/docs/README.md#validator), and [`Committee`](./ssv/docs/README.md#committee) structures.

f(x) = a0 + a1X + a2X^2+a3X^3 + ... + ak-1X^(k-1)\
f(0) = a0 = secret\
Share1 = f(NodeID1)\
Share1 = f(NodeID1)\
...
## Spec tests

### Spec tests
The [spec tests](ssv/spectest) are a generated as a json file that can be run in any implementation. They test the various flows within the SSV package, treating the consensus protocol as as black box.
The [spec tests](ssv/spectest) are generated as a json file that can be run in any implementation. They test the various flows within the SSV package, treating the consensus protocol as as black box.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be run in any implementation -> can be run in against implementation

To generate all json spec tests, run:

```console
foo@bar:~$ go generate ./...
foo@bar:~$ make generate-jsons
```
Then run all tests with

```console
foo@bar:~$ make test
```
Then run all tests
Loading