Skip to content

Commit

Permalink
Chores grooming (#11)
Browse files Browse the repository at this point in the history
* change sever to operator and clint to initiator

* update readme about operator`s priv key

* change plaintext password to file

* add logs about message signature verification

* change fork hex to name

* add identity to initiator

* add security notes to readme

* update flags + docker

* rename project

* readme update

* fix tests + work on comments

* add test of initiator identity

* update test operator

* add linter + fixes

* comment fixes

* update configs and paths to store

* readme update

* add .dockerignore

* add logging to file same as ssv

* update board logger

* readme update

* - Break dkg function into smaller functions
- add tests for these funcs
- fix logs

* update tests

* update tests

* add unhappy flow

* add threshold tests

* clean up

* fix makefile

* add more threshold checks for 4 ops

* groom log reports

* minor fix

* add test shares order ar ssv payload

* move NewID to crypto

* remove VerifyFunc

* move VerifyInitiatorMessage to state

* removed rsa priv from local owner

* logs path

* change payload strcuture

* fix payload to v4

* correct help for flag

* set default nonce to 0

* fixed integration tests to new format

* added single operator/initiator to Makefile

* change withdrawl address to be ETH1

* fix test to look for the ETH1 addr

* add hexToAddress function with error handling

* add error wrapper at state

* fix operator  test

* - change ssvpayload name to keyshares
- change deposit and keyshares results param names

* change error to show bad sigs

---------

Co-authored-by: y0sher <[email protected]>
  • Loading branch information
pavelkrolevets and y0sher committed Oct 10, 2023
1 parent 97e1ff6 commit 214ec16
Show file tree
Hide file tree
Showing 75 changed files with 3,592 additions and 4,428 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./bin/
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
bin/
bin/
.vscode/
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
FROM golang:1.20
FROM golang:1.20-alpine3.17
RUN apk add build-base

WORKDIR /

COPY go.mod go.sum ./
RUN go mod download
RUN go mod download && go mod verify

COPY ./ ./

# Build
RUN CGO_ENABLED=1 GOOS=linux go build -o /app /cmd/dkgcli/dkgcli.go
RUN CGO_ENABLED=1 GOOS=linux go build -o /app /cmd/ssv-dkg/ssv-dkg.go


EXPOSE 3030
62 changes: 48 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,79 @@
# with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make.

.PHONY: dkgcli test clean build docker-build
.PHONY: install clean build test docker-build-image docker-operators docker-initiator mockgen-install lint-prepare lint

GOBIN = ./build/bin
GO ?= latest
GORUN = env GO111MODULE=on go run
GOINSTALL = env GO111MODULE=on go install -v
GOTEST = env GO111MODULE=on go test -v
# Name of the Go binary output
BINARY_NAME=./bin/dkgcli
BINARY_NAME=./bin/ssv-dkg
# Docker image name
DOCKER_IMAGE=ssv-dkg-tool
DOCKER_IMAGE=ssv-dkg

install:
$(GOINSTALL) cmd/dkgcli/dkgcli.go
$(GOINSTALL) cmd/ssv-dkg/ssv-dkg.go
@echo "Done building."
@echo "Run dkgcli to launch the tool."
@echo "Run ssv-dkg to launch the tool."

clean:
env GO111MODULE=on go clean -cache

# Recipe to compile the Go program
build:
@echo "Building Go binary..."
go build -o $(BINARY_NAME) ./cmd/dkgcli/dkgcli.go
go build -o $(BINARY_NAME) ./cmd/ssv-dkg/ssv-dkg.go

# Recipe to run tests
test:
@echo "running tests"
go test -p 1 ./...
go test -v -p 1 ./...

# Recipe to build the Docker image
docker-build:
docker-build-image:
@echo "Building Docker image..."
docker build -t $(DOCKER_IMAGE) .

docker-servers:
@echo "Running servers in docker demo"
docker-compose up --build server1 server2 server3 server4
docker-demo-operators:
@echo "Running operators in docker demo"
docker-compose up --build operator1 operator2 operator3 operator4

docker-client:
@echo "Running client in docker demo"
docker-compose up --build client
docker-demo-initiator:
@echo "Running initiator in docker demo"
docker-compose up --build initiator

docker-operator:
@echo "Running operator docker, make sure to update ./examples/config/operator1.example.yaml"
docker run -d \
--name svv-dkg-operator \
-p 3030:3030 \
-v $(shell pwd)/examples:/data \
--entrypoint /app \
$(DOCKER_IMAGE):latest \
start-operator --configPath /data/config/operator1.example.yaml

docker-initiator:
@echo "Running initiator docker, make sure to update ./examples/config/initiator.example.yaml"
docker run -d \
--name ssv-dkg-initiator \
-v $(shell pwd)/examples:/data \
--entrypoint /app \
$(DOCKER_IMAGE):latest \
init --configPath /data/config/initiator.example.yaml

mockgen-install:
go install github.com/golang/mock/[email protected]
@which mockgen || echo "Error: ensure `go env GOPATH` is added to PATH"

lint-prepare:
@echo "Preparing Linter"
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s latest

lint:
./bin/golangci-lint run -v ./...
@if [ ! -z "${UNFORMATTED}" ]; then \
echo "Some files requires formatting, please run 'go fmt ./...'"; \
exit 1; \
fi
147 changes: 106 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,136 @@
# ssv-dkg-tool
# ssv-dkg

## Architecture
### Build

```sh
make install
```

### Operators data

The data of the operators (ID, IP, Pubkey) can be collected in any way, for example a central server that you can pull the data from, or a preset file where all operators data exist.

### Build

```sh
make install
Information about operators can be collected at `json` file and supplied to initiator to use for a key generation.

Operators info file example (`./examples/operators_integration.json`):

```json
[
{
"id": 1,
"public_key": "LS0tLS1CRUdJTiBSU0....",
"ip": "http://localhost:3030"
},
{
"id": 2,
"public_key": "LS0tLS1CRUdJTiB....",
"ip": "http://localhost:3031"
}
]
```

### Server
### Operator

The dkg server is ran by a SSV operator, an Operator RSA private key is a requirement.
The server is able to participate in multiple instances in parallel.
Whenever the server receives a message it directs it to the right instance by the identifier, and respond with an answer.
The dkg-operator is ran by a SSV operator, an Operator RSA private key is a requirement.
The operator is able to participate in multiple DKG ceremonies in parallel.

Start a DKG server
NOTE: ssv-dkg tool is using an ssv operator private key file. Encrypted and plintext versiaons are supported. If `password` parameter is provided then the ssv-dkg tool assumes that the operator`s RSA key is encrypted, if not then it assumes that the key is provided as plaintext.

#### Start a DKG-operator

```sh
dkgcli start-dkg-server --privKey ./examples/server1/encrypted_private_key.json --port 3030 --password 12345678 --storeShare true
ssv-dkg start-operator \
--privKey ./examples/operator1/encrypted_private_key.json \
--port 3030 \
--password ./password \
--storeShare true \
--logLevel info \
--logFormat json \
--logLevelFormat capitalColor \
--logFilePath ./operator1_logs/debug.log

### where
--privKey ./encrypted_private_key.json # path to base 64 encoded RSA private key in PKCS #1, ASN.1 DER form.
--privKey ./encrypted_private_key.json # path to ssv operator`s private key
--port 3030 # port for listening messages
--password: 12345678 # password for encrypted keys
--storeShare # store created bls key share to a file for later reuse
--password: ./password # path to password file to decrypt the key
--storeShare: true # store created bls key share to a file for later reuse if needed
--logLevel: info # logger's log level (info/debug/
--logFormat: json # logger's encoding, valid values are 'json' (default) and 'console'
--logLevelFormat: capitalColor # logger's level format, valid values are 'capitalColor' (default), 'capital' or 'lowercase''
--logFilePath: ./operator1_logs/debug.log # a file path to write logs into
```

Its also possible to use yaml configuration file `./config/operator.yaml` for parameters. `dkgcli` will be looking for this file at `./config/` folder.
Its also possible to use yaml configuration file `./config/operator.yaml` for parameters. `ssv-dkg` will be looking for the config file at `./config/` folder.

Example:

```yaml
privKey: ./encrypted_private_key.json
password: 12345678
password: ./password
port: 3030
storeShare: true
logLevel: info
logFormat: json
logLevelFormat: capitalColor
logFilePath: ./operator1_logs/debug.log
```
When using configuration file, run:
```sh
dkgcli start-dkg-server
ssv-dkg start-operator --configPath "/examples/config/operator4.example.yaml"
```

### Initiator of DKG key generation
### Initiator

The initiator uses `init` to create the initial details needed to run DKG between all operators.

The initiator uses `init-dkg` to create the initial details needed to run DKG between all operators.
Generate initiator identity RSA key pair:

```sh
dkgcli init-dkg \
ssv-dkg generate-initiator-keys --password 12345678
```

This will create `encrypted_private_key.json` with encrypted by password RSA key pair
Write down `password` in any text file, for example to `./password`

Run:

```sh
ssv-dkg init \
--operatorIDs 1,2,3,4 \
--operatorsInfoPath ./examples/operators_integration.json \
--operatorsInfoPath ./operators_integration.json \
--owner 0x81592c3de184a3e2c0dcb5a261bc107bfa91f494 \
--nonce 4 \
--withdrawAddress 0000000000000000000000000000000000000009 \
--fork 00000000
--depositResultsPath deposit.json
--ssvPayloadResultsPath payload.json
--fork "mainnet" \
--depositOutputPath deposit.json \
--keysharesOutputPath payload.json \
--initiatorPrivKey ./encrypted_private_key.json \
--initiatorPrivKeyPassword ./password \
--logLevel info \
--logFormat json \
--logLevelFormat capitalColor \
--logFilePath ./initiator_logs/debug.log

#### where
--operatorIDs 1,2,3,4 # operator IDs which will be used for a DKG ceremony
--operatorsInfoPath ./examples/operators_integration.json # path to info about operators - ID,base64(RSA pub key),
--operatorsInfoPath ./operators_integration.json # path to operators info ID,base64(RSA pub key),
--owner 0x81592c3de184a3e2c0dcb5a261bc107bfa91f494 # owner address for the SSV contract
--nonce 4 # owner nonce for the SSV contract
--fork "00000000" # fork id bytes in HEX
--depositResultsPath # path to store the result file
--ssvPayloadResultsPath # path to store ssv contract payload file
--withdrawAddress # Reward payments of excess balance over 32 ETH will automatically and regularly be sent to a withdrawal address linked to each validator, once provided by the user. Users can also exit staking entirely, unlocking their full validator balance.
--fork "mainnet" # fork name: mainnet, prater, or now_test_network
--depositOutputPath: ./output/ # path and filename to store the staking deposit file
--keysharesOutputPath: ./output/ # path and filename to store ssv contract payload file
--initiatorPrivKey ./encrypted_private_key.json # path to ssv initiators`s private key
--initiatorPrivKeyPassword: ./password # path to password file to decrypt the key
--logLevel: info # logger's log level (info/debug/
--logFormat: json # logger's encoding, valid values are 'json' (default) and 'console'
--logLevelFormat: capitalColor # logger's level format, valid values are 'capitalColor' (default), 'capital' or 'lowercase''
--logFilePath: ./initiator_logs/debug.log # a file path to write logs into
```

Its also possible to use yaml configuration file `./config/initiator.yaml` for parameters. `dkgcli` will be looking for this file at `./config/` folder.
Its also possible to use yaml configuration file `./config/initiator.yaml` for parameters. `ssv-dkg` will be looking for this file at `./config/` folder at the same root as the binary.

Example:

Expand All @@ -82,29 +141,35 @@ owner: "0x81592c3de184a3e2c0dcb5a261bc107bfa91f494"
nonce: 4
fork: "00000000"
operatorsInfoPath: ./examples/operators_integration.json
depositResultsPath: ./deposit.json
ssvPayloadResultsPath: ./payload.json
depositOutputPath: ./output/
keysharesOutputPath: ./output/
privKey: ./encrypted_private_key.json
password: ./password
```
When using configuration file, run:
```sh
dkgcli init-dkg
ssv-dkg init --configPath /examples/config/initiator.example.yaml
```

**_NOTE: Threshold is computed automatically using 3f+1 tolerance._**

### Generate RSA operator key
---

```sh
./dkgcli generate-operator-keys --password 12345678
```
### Security notes

---
Here we explain how we secure the communication between DKG ceremony initiator and operators

1. Initiator is using RSA key (2048 bits) to sign init message sent to operators. Upon receiving operators verify the sig using pub key at init message. If the sig is valid, operators store this pub key for further verification of messages coming from the initiator(s).
2. Operators are using RSA key (ssv operator key - 2048 bits) to sign every message sent back to initiator.
3. Initiator verifies every message incoming from any operator using ID and Public Key provided by operators info file, then initiator creates a combined message and signs it.
4. Operators verify each of the messages of other operators participating in the ceremony and verifies initiator`s signature of the combined message.
5. During the DKG protocol execution, the BLS auth scheme is used - G2 for its signature space and G1 for its public keys

### Schema
## Architecture

![flow](./imgs/DKGinit.drawio.png)
![flow](./docs/imgs/DKGinit.drawio.png)

#### Basic Flow Description:

Expand Down Expand Up @@ -210,7 +275,7 @@ Initial message fields:

### `Switch` instance management

The DKG server can handle multiple DKG instances, it saves up to MaxInstances(1024) up to `MaxInstanceTime` (5 minutes). If a new Init arrives we try to clean our list from instances older than `MaxInstanceTime` if we find any, we remove them and add the incoming, otherwise we respond with error that the maximum number of instances is already running.
The DKG-operator can handle multiple DKG instances, it saves up to MaxInstances(1024) up to `MaxInstanceTime` (5 minutes). If a new Init arrives we try to clean our list from instances older than `MaxInstanceTime` if we find any, we remove them and add the incoming, otherwise we respond with error that the maximum number of instances is already running.

### TODO:

Expand Down
11 changes: 5 additions & 6 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@ package cli
import (
"log"

"github.com/bloxapp/ssv-dkg-tool/cli/initiator"
"github.com/bloxapp/ssv-dkg-tool/cli/operator"
"github.com/bloxapp/ssv-dkg/cli/initiator"
"github.com/bloxapp/ssv-dkg/cli/operator"
"github.com/spf13/cobra"
"go.uber.org/zap"
)

func init() {
RootCmd.AddCommand(initiator.StartDKG)
RootCmd.AddCommand(operator.StartDKGServer)
RootCmd.AddCommand(operator.GenerateOperatorKeysCmd)
RootCmd.AddCommand(operator.ExportKeysCmd)
RootCmd.AddCommand(initiator.GenerateInitiatorKeysCmd)
RootCmd.AddCommand(operator.StartDKGOperator)
}

// RootCmd represents the root command of DKG-tool CLI
var RootCmd = &cobra.Command{
Use: "dkgcli",
Use: "ssv-dkg",
Short: "CLI for running Distributed Key Generation protocol",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
},
Expand Down
Loading

0 comments on commit 214ec16

Please sign in to comment.