Skip to content

Commit

Permalink
chore: post review fixes and clean up (#6)
Browse files Browse the repository at this point in the history
* add disclaimer + makefile

* json operators file

* remove .idea folder

* add Makefile

* logs cleanup

* add test and fix 2 bugs.

* add threshold compute + checks for operators

* add paragraph about instance managment to readme

* readme update

* fix tests

---------

Co-authored-by: pavelkrolevets <[email protected]>
  • Loading branch information
y0sher and pavelkrolevets authored Sep 11, 2023
1 parent f3a5b07 commit 0e15ffa
Show file tree
Hide file tree
Showing 26 changed files with 478 additions and 169 deletions.
8 changes: 0 additions & 8 deletions .idea/.gitignore

This file was deleted.

1 change: 0 additions & 1 deletion .idea/.name

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

9 changes: 0 additions & 9 deletions .idea/ssv-dkg-tool.iml

This file was deleted.

42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This Makefile is meant to be used by people that do not usually work
# 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

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
# Docker image name
DOCKER_IMAGE=ssv-dkg-tool

install:
$(GOINSTALL) cmd/dkgcli/dkgcli.go
@echo "Done building."
@echo "Run dkgcli 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

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

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

docker-demo:
@echo "Running docker compose demo"
docker-compose up
118 changes: 64 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The data of the operators (ID, IP, Pubkey) can be collected in any way, for exam
### Build

```sh
go build cmd/dkgcli/dkgcli.go
make install
```

### Server
Expand All @@ -21,60 +21,79 @@ Whenever the server receives a message it directs it to the right instance by th
Start a DKG server

```sh
./dkgcli start-dkg-server --privKey ./examples/server1/encrypted_private_key.json --port 3030 --password 12345678
dkgcli start-dkg-server --privKey ./examples/server1/encrypted_private_key.json --port 3030 --password 12345678 --storeShare true

### where
--privKey ./examples/server1/key # path to base 64 encoded RSA private key in PKCS #1, ASN.1 DER form.
--privKey ./encrypted_private_key.json # path to base 64 encoded RSA private key in PKCS #1, ASN.1 DER form.
--port 3030 # port for listening messages
--paseord 12345678 # password for encrypted keys
--password: 12345678 # password for encrypted keys
--storeShare # store created bls key share to a file for later reuse
```

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

Example:

```yaml
privKey: ./examples/server1/encrypted_private_key.json
privKey: ./encrypted_private_key.json
password: 12345678
port: 3030
storeShare: true
```
When using configuration file, run:
```sh
dkgcli start-dkg-server
```

### Initiator of DKG key generation

The initiator uses `ssv-dkg-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.

```sh
./dkgcli init-dkg \
dkgcli init-dkg \
--operatorIDs 1,2,3,4 \
--operatorsInfoPath ./examples/operators_integration.csv \
--operatorsInfoPath ./examples/operators_integration.json \
--owner 0x81592c3de184a3e2c0dcb5a261bc107bfa91f494 \
--nonce 1 \
--threshold 3 \
--withdrawPublicKey 0100000000000000000000001d2f14d2dffee594b4093d42e4bc1b0ea55e8aa7 \
--nonce 4 \
--withdrawAddress 0000000000000000000000000000000000000009 \
--fork 00000000
--depositResultsPath deposit.json
--ssvPayloadResultsPath payload.json
#### where
--operatorIDs 1,2,3,4 # operator IDs which will be used for a DKG ceremony
--operatorsInfoPath ./examples/operators_integration.csv # path to info about operators - ID,base64(RSA pub key),
--threshold 3 # threshold set for a master signature - if T out on N signatures provided the master signature will be recovered
--operatorsInfoPath ./examples/operators_integration.json # path to info about operators - ID,base64(RSA pub key),
--owner 0x81592c3de184a3e2c0dcb5a261bc107bfa91f494 # owner address for the SSV contract
--nonce 1 # owner nonce 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
```

Its also possible to use yaml configuration file `./config/initiator.yaml` for parameters. `dkgcli` will be looking for this file at `./config/` folder.

Example:

```yaml
threshold: 4
operatorIDs: [1, 2, 3, 4]
withdrawAddress: "0100000000000000000000001d2f14d2dffee594b4093d42e4bc1b0ea55e8aa7"
withdrawAddress: "0000000000000000000000000000000000000009"
owner: "0x81592c3de184a3e2c0dcb5a261bc107bfa91f494"
nonce: 4
fork: "00000000"
operatorsInfoPath: ./examples/operators_integration.csv
operatorsInfoPath: ./examples/operators_integration.json
depositResultsPath: ./deposit.json
ssvPayloadResultsPath: ./payload.json
```
When using configuration file, run:
```sh
dkgcli init-dkg
```

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

### Generate RSA operator key

```sh
Expand Down Expand Up @@ -189,76 +208,67 @@ Initial message fields:
Nonce int
```

### `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.

### TODO:

- [x] Complete design with flows and structure
- [x] output - signed ssv deposit data + encrypted shares for SSV contract
- [x] verification of ssv deposit data and encrypted shares
- [ ] existing validator public key resharing
- [ ] private key recreation from shares (in case of switch to a standard ETH validator)
- [ ] CLI for initiator and operators
- [ ] storage for initiator and keystore for operators
- [ ] more testing
- [ ] logging
- [x] CLI for initiator and operators
- [x] keystore for operators
- [x] more testing
- [x] logging

### Additional:

- [ ] get existing pub key share by ID from operators
- [ ] limit max of operators (T-threshold min/max)
- [x] limit max of operators (T-threshold min/max)
- [x] secure the communication between initiator and operators

### Flow TODO Brakedown

---

- [~70%] New key generation
- [~100%] New key generation

#### Round 1

- [x] CLI for initiator
- [x] CLI for operator
- [ ] RSA secret storage for both initiator and operator
- [ ] Init message:
- [x] RSA secret storage for operator
- [x] Init message:
- [x] Message sig validation
- [x] Init message owner + nonce fields. ID is random UUID
- [ ] Timeouts
- [ ] Error handling
- [ ] Exchange message:
- [x] Timeouts
- [x] Error handling
- [x] Exchange message:
- [x] Message sig validation
- [ ] Secret RSA key storage
- [ ] Timeouts
- [ ] Error handling
- [ ] Code refactoring
- [ ] Unit tests
- [ ] integration tests
- [x] Timeouts
- [x] Error handling
- [x] Code refactoring
- [x] Unit tests
- [x] integration tests

#### Round 2

- [x] Deal message:
- [x] Result message:
- [ ] Secure storage for key shares and DKG result (keystore + db) + recover option
- [x] Storage for key shares and DKG result
- [x] Validate signature shares + validator pub key + pub and encrypted shares at initiator
- [ ] Timeouts
- [ ] Code refactoring
- [ ] Error handling
- [ ] Unit tests
- [x] Timeouts
- [x] Code refactoring
- [x] Error handling
- [x] Unit tests

---

- [0%] Key resharing (new operator keys but same validator pub key) - implemented 0%
- [50%] Key resharing (new operator keys but same validator pub key) - implemented 0%

- [ ] CLI command and message to initiate resharing protocol
- [ ] Handlers of DKG key resharing messages exchange
- [x] CLI command and message to initiate resharing protocol
- [x] Handlers of DKG key resharing messages exchange
- [ ] Store new keys, update storage at operators
- [ ] Error handling
- [ ] Unit tests

---

- [0%] Private key recreation from shares at initiator - implemented 0%
- [ ] CLI command and message to initiate reconstruction of the key from shares
- [ ] Handlers to send encrypted with RSA pub key shares to initiator
- [ ] DKG private key recovery from shares
- [ ] Keystore storage of validator priv key
- [ ] Error handling
- [ ] Unit tests
7 changes: 0 additions & 7 deletions build_for_testing.sh

This file was deleted.

32 changes: 24 additions & 8 deletions cli/initiator/initiator.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
)

func init() {
flags.ThresholdFlag(StartDKG)
flags.WithdrawAddressFlag(StartDKG)
flags.OperatorsInfoFlag(StartDKG)
flags.OperatorIDsFlag(StartDKG)
Expand All @@ -30,7 +29,6 @@ func init() {
flags.ForkVersionFlag(StartDKG)
flags.AddDepositResultStorePathFlag(StartDKG)
flags.AddSSVPayloadResultStorePathFlag(StartDKG)
viper.BindPFlag("threshold", StartDKG.PersistentFlags().Lookup("threshold"))
viper.BindPFlag("withdrawAddress", StartDKG.PersistentFlags().Lookup("withdrawAddress"))
viper.BindPFlag("operatorIDs", StartDKG.PersistentFlags().Lookup("operatorIDs"))
viper.BindPFlag("operatorsInfoPath", StartDKG.PersistentFlags().Lookup("operatorsInfoPath"))
Expand Down Expand Up @@ -87,7 +85,13 @@ var StartDKG = &cobra.Command{
if operatorFile == "" {
logger.Fatal("failed to get operator info file path flag value", zap.Error(err))
}
opMap, err := load.Operators(operatorFile)

opsfile, err := os.ReadFile(operatorFile)
if err != nil {
logger.Fatal("failed to read operator info file", zap.Error(err))
}

opMap, err := load.LoadOperatorsJson(opsfile)
if err != nil {
logger.Fatal("Failed to load operators: ", zap.Error(err))
}
Expand All @@ -105,10 +109,6 @@ var StartDKG = &cobra.Command{
if withdrawAddr == "" {
logger.Fatal("failed to get withdrawal address flag value", zap.Error(err))
}
threshold := viper.GetUint64("threshold")
if threshold < 1 {
logger.Fatal("failed to get threshold flag value", zap.Error(err))
}
forkHex := viper.GetString("fork")
if forkHex == "" {
logger.Fatal("failed to get fork version flag value", zap.Error(err))
Expand Down Expand Up @@ -142,7 +142,7 @@ var StartDKG = &cobra.Command{
if err != nil {
logger.Fatal("failed to decode withdrawal public key", zap.Error(err))
}
depositData, keyShares, err := dkgClient.StartDKG(withdrawPubKey, parts, threshold, fork, forkName, common.HexToAddress(owner), nonce)
depositData, keyShares, err := dkgClient.StartDKG(withdrawPubKey, parts, fork, forkName, common.HexToAddress(owner), nonce)

if err != nil {
logger.Fatal("failed to initiate DKG ceremony", zap.Error(err))
Expand All @@ -161,6 +161,22 @@ var StartDKG = &cobra.Command{
}

logger.Info("DKG protocol finished successfull")
fmt.Println(`
▓█████▄ ██▓ ██████ ▄████▄ ██▓ ▄▄▄ ██▓ ███▄ ▄███▓▓█████ ██▀███
▒██▀ ██▌▓██▒▒██ ▒ ▒██▀ ▀█ ▓██▒ ▒████▄ ▓██▒▓██▒▀█▀ ██▒▓█ ▀ ▓██ ▒ ██▒
░██ █▌▒██▒░ ▓██▄ ▒▓█ ▄ ▒██░ ▒██ ▀█▄ ▒██▒▓██ ▓██░▒███ ▓██ ░▄█ ▒
░▓█▄ ▌░██░ ▒ ██▒▒▓▓▄ ▄██▒▒██░ ░██▄▄▄▄██ ░██░▒██ ▒██ ▒▓█ ▄ ▒██▀▀█▄
░▒████▓ ░██░▒██████▒▒▒ ▓███▀ ░░██████▒▓█ ▓██▒░██░▒██▒ ░██▒░▒████▒░██▓ ▒██▒
▒▒▓ ▒ ░▓ ▒ ▒▓▒ ▒ ░░ ░▒ ▒ ░░ ▒░▓ ░▒▒ ▓▒█░░▓ ░ ▒░ ░ ░░░ ▒░ ░░ ▒▓ ░▒▓░
░ ▒ ▒ ▒ ░░ ░▒ ░ ░ ░ ▒ ░ ░ ▒ ░ ▒ ▒▒ ░ ▒ ░░ ░ ░ ░ ░ ░ ░▒ ░ ▒░
░ ░ ░ ▒ ░░ ░ ░ ░ ░ ░ ░ ▒ ▒ ░░ ░ ░ ░░ ░
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
░ ░
This tool was not audited.
When using distributed key generation you understand all the risks involved with
experimental cryptography.
`)
},
}

Expand Down
4 changes: 2 additions & 2 deletions cli/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var StartDKGServer = &cobra.Command{
if err := logging.SetGlobalLogger("debug", "capital", "console"); err != nil {
log.Fatal(err)
}
logger := zap.L().Named(cmd.Short)
logger := zap.L().Named("dkg-server")

viper.SetConfigName("operator")
viper.SetConfigType("yaml")
Expand Down Expand Up @@ -84,7 +84,7 @@ var StartDKGServer = &cobra.Command{
if err != nil {
logger.Fatal(err.Error())
}
logger.Info("Starting DKG instance at ", zap.Uint64("port", port), zap.String("public key", string(pubKey)))
logger.Info("starting DKG server", zap.Uint64("port", port), zap.String("public key", string(pubKey)))
if err := srv.Start(uint16(port)); err != nil {
log.Fatalf("Error in server %v", err)
}
Expand Down
5 changes: 2 additions & 3 deletions config/initiator.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
threshold: 3
operatorIDs: [1, 2, 3, 4]
withdrawAddress: "0x0000000000000000000000000000000000000009"
withdrawAddress: "0000000000000000000000000000000000000009"
owner: "0x81592c3de184a3e2c0dcb5a261bc107bfa91f494"
nonce: 4
fork: "00000000"
operatorsInfoPath: ./examples/operators_integration.csv
operatorsInfoPath: ./examples/operators_integration.json
depositResultsPath: ./deposit.json
ssvPayloadResultsPath: ./payload.json
Loading

0 comments on commit 0e15ffa

Please sign in to comment.