Skip to content

Commit

Permalink
Re-sign and re-share using proofs and EIP1271 signature (#100)
Browse files Browse the repository at this point in the history
* initial resigning

* add routes + flow

* working resigning with result validation

* fix tests

* add integration tests for resiging

* update integration tests to use cli commands

* initial spec alignment + reshare module

* working resharing

* add EIP1271 owner signature verification

* update resharing + add docker demo for resharing

* add reshare threshold + integration tests

* add threshold integration tests

* fix logic at message processing

* add eip1271 sig integration tests

* add eip1271 sig verification to resign + integration/docker tests

* add eip1271 sig resign test + bulk tests

* fix/update unit + integration tests

* update tests + fix bls set

* PR comments resolved #1

* PR comments resolved #2

* PR comments resolved #3

* PR comments resolved #4

* PR comments resolved #5

* PR comments resolved #6

* fix tests + docker build update

* update CI

* fix test

* lint

* add more reshare integration tests

* go 1.23

* add unit tests for join/disjoin sets of operators

* lint

* add proofs validation before sending + more reshare threshold integration tests

* fix unit tests

* lint

* fix review comments

* remove typo and unused struct

---------

Co-authored-by: Matus Kysel <[email protected]>
  • Loading branch information
pavelkrolevets and MatusKysel committed Sep 3, 2024
1 parent ace61ed commit 8d55ffd
Show file tree
Hide file tree
Showing 105 changed files with 6,980 additions and 714 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.20"
go-version: "1.23"

- name: Build
run: go build -v ./...

- name: Test
run: go test ./... -timeout 3600s
run: go run gotest.tools/gotestsum@latest --format pkgname -- -timeout=3600s ./...

- name: Critic
run: go install -v github.com/go-critic/go-critic/cmd/gocritic@latest && gocritic check ./...
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
bin/
.vscode/
examples/*/output/*
integration_test/output

######## Node
# Logs
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Use golang base image
FROM golang:1.20-alpine3.18 as build
FROM golang:1.23.0-alpine3.20 as build

WORKDIR /ssv-dkg

Expand Down Expand Up @@ -27,7 +27,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
./cmd/ssv-dkg

# Final stage
FROM alpine:3.18
FROM alpine:3.20
WORKDIR /ssv-dkg

# Install openssl
Expand Down
16 changes: 14 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ build:
# Recipe to run tests
test:
@echo "running tests"
go run gotest.tools/gotestsum@latest --format testname
go run gotest.tools/gotestsum@latest --format pkgname --jsonfile test-output.log -- -timeout=3600s ./...

# Recipe to build the Docker image
docker-build-image:
Expand All @@ -40,16 +40,28 @@ docker-build-image:

docker-demo-operators:
@echo "Running operators in docker demo"
docker-compose up --build operator1 operator2 operator3 operator4 operator5 operator6 operator7 operator8
docker-compose up --build operator1 operator2 operator3 operator4 operator5 operator6 operator7 operator8 operator9 operator10 operator11 operator12 operator13

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

docker-demo-resign:
@echo "Running re-sign ceremony in docker demo"
docker-compose up --build resign

docker-demo-reshare:
@echo "Running re-share ceremony in docker demo"
docker-compose up --build reshare

docker-demo-ping:
@echo "Running ping operators in docker demo"
docker-compose up --build ping

docker-demo-ethnode:
@echo "Running ethereum node in docker demo"
docker-compose up --build ethnode

docker-operator:
@echo "Running operator docker, make sure to update ./examples/operator1/congig/config.yaml"
docker run \
Expand Down
4 changes: 4 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ func init() {
RootCmd.AddCommand(operator.StartDKGOperator)
RootCmd.AddCommand(initiator.HealthCheck)
RootCmd.AddCommand(verify.Verify)
RootCmd.AddCommand(initiator.StartResigning)
RootCmd.AddCommand(initiator.StartReshare)
}

// RootCmd represents the root command of DKG-tool CLI
Expand All @@ -32,6 +34,8 @@ func Execute(appName, version string) {
RootCmd.Version = version
initiator.HealthCheck.Version = version
initiator.StartDKG.Version = version
initiator.StartResigning.Version = version
initiator.StartReshare.Version = version
operator.StartDKGOperator.Version = version
if err := RootCmd.Execute(); err != nil {
log.Fatal("failed to execute root command", zap.Error(err))
Expand Down
31 changes: 31 additions & 0 deletions cli/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
const (
withdrawAddress = "withdrawAddress"
operatorIDs = "operatorIDs"
newOperatorIDs = "newOperatorIDs"
operatorsInfo = "operatorsInfo"
operatorsInfoPath = "operatorsInfoPath"
privKey = "privKey"
Expand All @@ -29,6 +30,10 @@ const (
clientCACertPath = "clientCACertPath"
serverTLSCertPath = "serverTLSCertPath"
serverTLSKeyPath = "serverTLSKeyPath"
proofsFilePath = "proofsFilePath"
ethKeystorePath = "ethKeystorePath"
ethKeystorePass = "ethKeystorePass"
ethEndpointURL = "ethEndpointURL"
)

// WithdrawAddressFlag adds withdraw address flag to the command
Expand All @@ -41,6 +46,11 @@ func OperatorIDsFlag(c *cobra.Command) {
AddPersistentStringSliceFlag(c, operatorIDs, []string{"1", "2", "3"}, "Operator IDs", false)
}

// newOperatorIDsFlag adds new operators IDs flag to the command
func NewOperatorIDsFlag(c *cobra.Command) {
AddPersistentStringSliceFlag(c, newOperatorIDs, []string{"1", "2", "3"}, "New operator IDs for resharing ceremony", false)
}

// OperatorsInfoFlag adds path to operators' ifo file flag to the command
func OperatorsInfoFlag(c *cobra.Command) {
AddPersistentStringFlag(c, operatorsInfo, "", "Raw JSON string operators' public keys, IDs and IPs file e.g. `{ 1: { publicKey: XXX, id: 1, ip: 10.0.0.1:3033 }`", false)
Expand Down Expand Up @@ -136,6 +146,27 @@ func OperatorIDFlag(c *cobra.Command) {
AddPersistentIntFlag(c, operatorID, 0, "Operator ID", false)
}

// ProofsFilePath add file path to proofs flag to the command
func ProofsFilePath(c *cobra.Command) {
AddPersistentStringFlag(c, proofsFilePath, "proofs.json", "Path to proofs file", false)
}

// KeystoreFilePath
func KeystoreFilePath(c *cobra.Command) {
AddPersistentStringFlag(c, ethKeystorePath, "keystore.json", "Path to ethereum keystore json file", false)
}

// KeystoreFilePass
func KeystoreFilePass(c *cobra.Command) {
AddPersistentStringFlag(c, ethKeystorePass, "", "Password to decrypt ethereum keystore json file", false)
}


// EthEndpointURL
func EthEndpointURL(c *cobra.Command) {
AddPersistentStringFlag(c, ethEndpointURL, "http://127.0.0.1:8545", "Ethereum node endpoint URL", false)
}

// AddPersistentStringFlag adds a string flag to the command
func AddPersistentStringFlag(c *cobra.Command, flag, value, description string, isRequired bool) {
req := ""
Expand Down
21 changes: 10 additions & 11 deletions cli/initiator/initiator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import (
"fmt"
"log"

"github.com/sourcegraph/conc/pool"
"github.com/spf13/cobra"
"go.uber.org/zap"

e2m_core "github.com/bloxapp/eth2-key-manager/core"
cli_utils "github.com/bloxapp/ssv-dkg/cli/utils"
"github.com/bloxapp/ssv-dkg/pkgs/crypto"
"github.com/bloxapp/ssv-dkg/pkgs/initiator"
"github.com/bloxapp/ssv-dkg/pkgs/wire"
"github.com/sourcegraph/conc/pool"
"github.com/spf13/cobra"
"go.uber.org/zap"

spec "github.com/ssvlabs/dkg-spec"
)

const (
Expand Down Expand Up @@ -62,10 +62,9 @@ var StartDKG = &cobra.Command{
if err != nil {
logger.Fatal("πŸ˜₯ Failed to load operators: ", zap.Error(err))
}
logger.Info("πŸ”‘ opening initiator RSA private key file")
ethnetwork := e2m_core.MainNetwork
if cli_utils.Network != "now_test_network" {
ethnetwork = e2m_core.NetworkFromString(cli_utils.Network)
ethNetwork := e2m_core.NetworkFromString(cli_utils.Network)
if ethNetwork == "" {
logger.Fatal("πŸ˜₯ Cant recognize eth network")
}
// start the ceremony
ctx := context.Background()
Expand All @@ -79,10 +78,10 @@ var StartDKG = &cobra.Command{
return nil, err
}
// Create a new ID.
id := crypto.NewID()
id := spec.NewID()
nonce := cli_utils.Nonce + uint64(i)
// Perform the ceremony.
depositData, keyShares, proofs, err := dkgInitiator.StartDKG(id, cli_utils.WithdrawAddress.Bytes(), operatorIDs, ethnetwork, cli_utils.OwnerAddress, nonce)
depositData, keyShares, proofs, err := dkgInitiator.StartDKG(id, cli_utils.WithdrawAddress.Bytes(), operatorIDs, ethNetwork, cli_utils.OwnerAddress, nonce)
if err != nil {
return nil, err
}
Expand Down
125 changes: 125 additions & 0 deletions cli/initiator/reshare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package initiator

import (
"fmt"
"log"
"os"
"path/filepath"

e2m_core "github.com/bloxapp/eth2-key-manager/core"
cli_utils "github.com/bloxapp/ssv-dkg/cli/utils"
"github.com/bloxapp/ssv-dkg/pkgs/initiator"
"github.com/bloxapp/ssv-dkg/pkgs/wire"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/spf13/cobra"
"go.uber.org/zap"

spec "github.com/ssvlabs/dkg-spec"
)

func init() {
cli_utils.SetReshareFlags(StartReshare)
}

var StartReshare = &cobra.Command{
Use: "reshare",
Short: "Reshare an existing key to new operators",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println(`
β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–„ β–ˆβ–ˆ β–„β–ˆβ–€ β–„β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–€β–ˆβ–ˆβ–ˆ β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–‘ β–ˆβ–ˆ β–„β–„β–„ β–ˆβ–ˆβ–€β–ˆβ–ˆβ–ˆ β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–’β–ˆβ–ˆβ–€ β–ˆβ–ˆβ–Œ β–ˆβ–ˆβ–„β–ˆβ–’ β–ˆβ–ˆβ–’ β–€β–ˆβ–’ β–“β–ˆβ–ˆ β–’ β–ˆβ–ˆβ–’β–“β–ˆ β–€ β–’β–ˆβ–ˆ β–’ β–“β–ˆβ–ˆβ–‘ β–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–„ β–“β–ˆβ–ˆ β–’ β–ˆβ–ˆβ–’β–“β–ˆ β–€
β–‘β–ˆβ–ˆ β–ˆβ–Œβ–“β–ˆβ–ˆβ–ˆβ–„β–‘ β–’β–ˆβ–ˆβ–‘β–„β–„β–„β–‘ β–“β–ˆβ–ˆ β–‘β–„β–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–‘ β–“β–ˆβ–ˆβ–„ β–’β–ˆβ–ˆβ–€β–€β–ˆβ–ˆβ–‘β–’β–ˆβ–ˆ β–€β–ˆβ–„ β–“β–ˆβ–ˆ β–‘β–„β–ˆ β–’β–’β–ˆβ–ˆβ–ˆ
β–‘β–“β–ˆβ–„ β–Œβ–“β–ˆβ–ˆ β–ˆβ–„ β–‘β–“β–ˆ β–ˆβ–ˆβ–“ β–’β–ˆβ–ˆβ–€β–€β–ˆβ–„ β–’β–“β–ˆ β–„ β–’ β–ˆβ–ˆβ–’β–‘β–“β–ˆ β–‘β–ˆβ–ˆ β–‘β–ˆβ–ˆβ–„β–„β–„β–„β–ˆβ–ˆ β–’β–ˆβ–ˆβ–€β–€β–ˆβ–„ β–’β–“β–ˆ β–„
β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–“ β–’β–ˆβ–ˆβ–’ β–ˆβ–„β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–€β–’ β–‘β–ˆβ–ˆβ–“ β–’β–ˆβ–ˆβ–’β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–‘β–“β–ˆβ–’β–‘β–ˆβ–ˆβ–“ β–“β–ˆ β–“β–ˆβ–ˆβ–’β–‘β–ˆβ–ˆβ–“ β–’β–ˆβ–ˆβ–’β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–’
β–’β–’β–“ β–’ β–’ β–’β–’ β–“β–’ β–‘β–’ β–’ β–‘ β–’β–“ β–‘β–’β–“β–‘β–‘β–‘ β–’β–‘ β–‘β–’ β–’β–“β–’ β–’ β–‘ β–’ β–‘β–‘β–’β–‘β–’ β–’β–’ β–“β–’β–ˆβ–‘β–‘ β–’β–“ β–‘β–’β–“β–‘β–‘β–‘ β–’β–‘ β–‘
β–‘ β–’ β–’ β–‘ β–‘β–’ β–’β–‘ β–‘ β–‘ β–‘β–’ β–‘ β–’β–‘ β–‘ β–‘ β–‘β–‘ β–‘β–’ β–‘ β–‘ β–’ β–‘β–’β–‘ β–‘ β–’ β–’β–’ β–‘ β–‘β–’ β–‘ β–’β–‘ β–‘ β–‘ β–‘
β–‘ β–‘ β–‘ β–‘ β–‘β–‘ β–‘ β–‘ β–‘ β–‘ β–‘β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘β–‘ β–‘ β–‘ β–’ β–‘β–‘ β–‘ β–‘
β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘
β–‘`)
if err := cli_utils.SetViperConfig(cmd); err != nil {
return err
}
if err := cli_utils.BindReshareFlags(cmd); err != nil {
return err
}
logger, err := cli_utils.SetGlobalLogger(cmd, "dkg-initiator")
if err != nil {
return err
}
defer func() {
if err := cli_utils.Sync(logger); err != nil {
log.Printf("Failed to sync logger: %v", err)
}
}()
logger.Info("πŸͺ› Initiator`s", zap.String("Version", cmd.Version))
opMap, err := cli_utils.LoadOperators(logger)
if err != nil {
logger.Fatal("πŸ˜₯ Failed to load operators: ", zap.Error(err))
}
oldOperatorIDs, err := cli_utils.StingSliceToUintArray(cli_utils.OperatorIDs)
if err != nil {
logger.Fatal("πŸ˜₯ Failed to load participants: ", zap.Error(err))
}
newOperatorIDs, err := cli_utils.StingSliceToUintArray(cli_utils.NewOperatorIDs)
if err != nil {
logger.Fatal("πŸ˜₯ Failed to load new participants: ", zap.Error(err))
}
// create a new ID for resharing
id := spec.NewID()
// create initiator instance
dkgInitiator, err := initiator.New(opMap.Clone(), logger, cmd.Version, cli_utils.ClientCACertPath)
if err != nil {
return err
}
signedProofs, err := wire.LoadProofs(cli_utils.ProofsFilePath)
if err != nil {
logger.Fatal("πŸ˜₯ Failed to read proofs json file:", zap.Error(err))
}
ethNetwork := e2m_core.NetworkFromString(cli_utils.Network)
if ethNetwork == "" {
logger.Fatal("πŸ˜₯ Cant recognize eth network")
}
// Open ethereum keystore
jsonBytes, err := os.ReadFile(cli_utils.KeystorePath)
if err != nil {
return err
}
keyStorePassword, err := os.ReadFile(filepath.Clean(cli_utils.KeystorePass))
if err != nil {
return fmt.Errorf("πŸ˜₯ Error reading password file: %s", err)
}
sk, err := keystore.DecryptKey(jsonBytes, string(keyStorePassword))
if err != nil {
return err
}
// Start the ceremony
depositData, keyShares, proof, err := dkgInitiator.StartResharing(id, oldOperatorIDs, newOperatorIDs, signedProofs[0], sk.PrivateKey, ethNetwork, cli_utils.WithdrawAddress[:], cli_utils.OwnerAddress, cli_utils.Nonce)
if err != nil {
logger.Fatal("πŸ˜₯ Failed to initiate DKG ceremony: ", zap.Error(err))
}
var depositDataArr []*wire.DepositDataCLI
var keySharesArr []*wire.KeySharesCLI
var proofs [][]*wire.SignedProof
depositDataArr = append(depositDataArr, depositData)
keySharesArr = append(keySharesArr, keyShares)
proofs = append(proofs, proof)
// Save results
logger.Info("🎯 All data is validated.")
if err := cli_utils.WriteResults(
logger,
depositDataArr,
keySharesArr,
proofs,
false,
1,
cli_utils.OwnerAddress,
cli_utils.Nonce,
cli_utils.WithdrawAddress,
cli_utils.OutputPath,
); err != nil {
logger.Fatal("Could not save results", zap.Error(err))
}
logger.Info("πŸš€ Resharing ceremony completed")
return nil
},
}
Loading

0 comments on commit 8d55ffd

Please sign in to comment.