diff --git a/Makefile b/Makefile index 775ba0af..76d1ecff 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # 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 docker-operators docker-initiator mockgen-install GOBIN = ./build/bin GO ?= latest @@ -17,7 +17,7 @@ DOCKER_IMAGE=ssv-dkg install: $(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 @@ -30,7 +30,7 @@ build: # 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: @@ -43,4 +43,8 @@ docker-operators: docker-initiator: @echo "Running initiator in docker demo" - docker-compose up --build initiator \ No newline at end of file + docker-compose up --build initiator + +mockgen-install: + go install github.com/golang/mock/mockgen@v1.6.0 + @which mockgen || echo "Error: ensure `go env GOPATH` is added to PATH" diff --git a/README.md b/README.md index 7c6c5562..7cf437de 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ NOTE: ssv-dkg tool is using an ssv operator private key file. Encrypted and plin #### Start a DKG-operator ```sh -ssv-dkg start-dkg-operator --privKey ./examples/operator1/encrypted_private_key.json --port 3030 --password ./password --storeShare true +ssv-dkg start-operator --privKey ./examples/operator1/encrypted_private_key.json --port 3030 --password ./password --storeShare true ### where --privKey ./encrypted_private_key.json # path to ssv operator`s private key @@ -62,12 +62,12 @@ storeShare: true When using configuration file, run: ```sh -ssv-dkg start-dkg-operator +ssv-dkg start-operator ``` ### Initiator -The initiator uses `init-dkg` to create the initial details needed to run DKG between all operators. +The initiator uses `init` to create the initial details needed to run DKG between all operators. Generate initiator identity RSA key pair: @@ -81,7 +81,7 @@ Write down `password` in any text file, for example to `./password` Run: ```sh -ssv-dkg init-dkg \ +ssv-dkg init \ --operatorIDs 1,2,3,4 \ --operatorsInfoPath ./operators_integration.json \ --owner 0x81592c3de184a3e2c0dcb5a261bc107bfa91f494 \ @@ -106,7 +106,7 @@ ssv-dkg init-dkg \ --initiatorPrivKeyPassword: ./password # path to password file to decrypt the key ``` -Its also possible to use yaml configuration file `./config/initiator.yaml` for parameters. `dkgcli` will be looking for this file at `./config/` folder at a same root as the binary. +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: @@ -126,7 +126,7 @@ password: ./password When using configuration file, run: ```sh -ssv-dkg init-dkg +ssv-dkg init ``` **_NOTE: Threshold is computed automatically using 3f+1 tolerance._** @@ -140,8 +140,8 @@ Here we explain how we secure the communication between DKG ceremony initiator a 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 signature of the message. -5. During the DKG protocol execution, the BLS auth scheme is being used - G2 for its signature space and G1 for its public key +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 ## Architecture diff --git a/cli/initiator/initiator.go b/cli/initiator/initiator.go index 9357326d..daab126e 100644 --- a/cli/initiator/initiator.go +++ b/cli/initiator/initiator.go @@ -46,7 +46,7 @@ func init() { } var StartDKG = &cobra.Command{ - Use: "init-dkg", + Use: "init", Short: "Initiates a DKG protocol", Run: func(cmd *cobra.Command, args []string) { fmt.Println(` diff --git a/cli/operator/operator.go b/cli/operator/operator.go index e646bc17..cc679356 100644 --- a/cli/operator/operator.go +++ b/cli/operator/operator.go @@ -29,7 +29,7 @@ func init() { } var StartDKGOperator = &cobra.Command{ - Use: "start-dkg-operator", + Use: "start-operator", Short: "Starts an instance of DKG operator", Run: func(cmd *cobra.Command, args []string) { fmt.Println(` diff --git a/docker-compose.yml b/docker-compose.yml index ab9f4af3..35be8bc1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: entrypoint: /app command: [ - "start-dkg-operator", + "start-operator", "--privKey", "/data/operator1/encrypted_private_key.json", "--password", @@ -27,7 +27,7 @@ services: entrypoint: /app command: [ - "start-dkg-operator", + "start-operator", "--privKey", "/data/operator2/encrypted_private_key.json", "--password", @@ -45,7 +45,7 @@ services: entrypoint: /app command: [ - "start-dkg-operator", + "start-operator", "--privKey", "/data/operator3/encrypted_private_key.json", "--password", @@ -63,7 +63,7 @@ services: entrypoint: /app command: [ - "start-dkg-operator", + "start-operator", "--privKey", "/data/operator4/encrypted_private_key.json", "--password", @@ -84,7 +84,7 @@ services: entrypoint: /app command: [ - "init-dkg", + "init", "--operatorIDs", "1,2,3,4", "--operatorsInfoPath", diff --git a/integration_test/integration_test.go b/integration_test/integration_test.go index d082558d..18dc647f 100644 --- a/integration_test/integration_test.go +++ b/integration_test/integration_test.go @@ -92,99 +92,99 @@ func TestHappyFlow(t *testing.T) { srv3.srv.Close() srv4.srv.Close() }) - // t.Run("test 7 operators happy flow", func(t *testing.T) { - // ops := make(map[uint64]initiator.Operator) - // srv1 := CreateOperator(t, 1) - // ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} - // srv2 := CreateOperator(t, 2) - // ops[2] = initiator.Operator{srv2.srv.URL, 2, &srv2.privKey.PublicKey} - // srv3 := CreateOperator(t, 3) - // ops[3] = initiator.Operator{srv3.srv.URL, 3, &srv3.privKey.PublicKey} - // srv4 := CreateOperator(t, 4) - // ops[4] = initiator.Operator{srv4.srv.URL, 4, &srv4.privKey.PublicKey} - // srv5 := CreateOperator(t, 5) - // ops[5] = initiator.Operator{srv5.srv.URL, 5, &srv5.privKey.PublicKey} - // srv6 := CreateOperator(t, 6) - // ops[6] = initiator.Operator{srv6.srv.URL, 6, &srv6.privKey.PublicKey} - // srv7 := CreateOperator(t, 7) - // ops[7] = initiator.Operator{srv7.srv.URL, 7, &srv7.privKey.PublicKey} - // // Initiator priv key - // _, pv, err := rsaencryption.GenerateKeys() - // require.NoError(t, err) - // priv, err := rsaencryption.ConvertPemToPrivateKey(string(pv)) - // clnt := initiator.New(priv, ops) - // withdraw := newEthAddress(t) - // owner := newEthAddress(t) - // depositData, ks, err := clnt.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) - // require.NoError(t, err) - // sharesDataSigned, err := hex.DecodeString(ks.Payload.Readable.Shares[2:]) - // require.NoError(t, err) - // pubkeyraw, err := hex.DecodeString(ks.Payload.Readable.PublicKey[2:]) - // require.NoError(t, err) - // testSharesData(t, ops, []*rsa.PrivateKey{srv1.privKey, srv2.privKey, srv3.privKey, srv4.privKey, srv5.privKey, srv6.privKey, srv7.privKey}, sharesDataSigned, pubkeyraw, owner, 0) - // testDepositData(t, depositData, withdraw.Bytes(), owner, 0) - // srv1.srv.Close() - // srv2.srv.Close() - // srv3.srv.Close() - // srv4.srv.Close() - // srv5.srv.Close() - // srv6.srv.Close() - // srv7.srv.Close() - // }) - // t.Run("test 12 operators happy flow", func(t *testing.T) { - // ops := make(map[uint64]initiator.Operator) - // srv1 := CreateOperator(t, 1) - // ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} - // srv2 := CreateOperator(t, 2) - // ops[2] = initiator.Operator{srv2.srv.URL, 2, &srv2.privKey.PublicKey} - // srv3 := CreateOperator(t, 3) - // ops[3] = initiator.Operator{srv3.srv.URL, 3, &srv3.privKey.PublicKey} - // srv4 := CreateOperator(t, 4) - // ops[4] = initiator.Operator{srv4.srv.URL, 4, &srv4.privKey.PublicKey} - // srv5 := CreateOperator(t, 5) - // ops[5] = initiator.Operator{srv5.srv.URL, 5, &srv5.privKey.PublicKey} - // srv6 := CreateOperator(t, 6) - // ops[6] = initiator.Operator{srv6.srv.URL, 6, &srv6.privKey.PublicKey} - // srv7 := CreateOperator(t, 7) - // ops[7] = initiator.Operator{srv7.srv.URL, 7, &srv7.privKey.PublicKey} - // srv8 := CreateOperator(t, 8) - // ops[8] = initiator.Operator{srv8.srv.URL, 8, &srv8.privKey.PublicKey} - // srv9 := CreateOperator(t, 9) - // ops[9] = initiator.Operator{srv9.srv.URL, 9, &srv9.privKey.PublicKey} - // srv10 := CreateOperator(t, 10) - // ops[10] = initiator.Operator{srv10.srv.URL, 10, &srv10.privKey.PublicKey} - // srv11 := CreateOperator(t, 11) - // ops[11] = initiator.Operator{srv11.srv.URL, 11, &srv11.privKey.PublicKey} - // srv12 := CreateOperator(t, 12) - // ops[12] = initiator.Operator{srv12.srv.URL, 12, &srv12.privKey.PublicKey} - // // Initiator priv key - // _, pv, err := rsaencryption.GenerateKeys() - // require.NoError(t, err) - // priv, err := rsaencryption.ConvertPemToPrivateKey(string(pv)) - // clnt := initiator.New(priv, ops) - // withdraw := newEthAddress(t) - // owner := newEthAddress(t) - // depositData, ks, err := clnt.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) - // require.NoError(t, err) - // sharesDataSigned, err := hex.DecodeString(ks.Payload.Readable.Shares[2:]) - // require.NoError(t, err) - // pubkeyraw, err := hex.DecodeString(ks.Payload.Readable.PublicKey[2:]) - // require.NoError(t, err) - // testSharesData(t, ops, []*rsa.PrivateKey{srv1.privKey, srv2.privKey, srv3.privKey, srv4.privKey, srv5.privKey, srv6.privKey, srv7.privKey, srv8.privKey, srv9.privKey, srv10.privKey, srv11.privKey, srv12.privKey}, sharesDataSigned, pubkeyraw, owner, 0) - // testDepositData(t, depositData, withdraw.Bytes(), owner, 0) - // srv1.srv.Close() - // srv2.srv.Close() - // srv3.srv.Close() - // srv4.srv.Close() - // srv5.srv.Close() - // srv6.srv.Close() - // srv7.srv.Close() - // srv8.srv.Close() - // srv9.srv.Close() - // srv10.srv.Close() - // srv11.srv.Close() - // srv12.srv.Close() - // }) + t.Run("test 7 operators happy flow", func(t *testing.T) { + ops := make(map[uint64]initiator.Operator) + srv1 := CreateOperator(t, 1) + ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} + srv2 := CreateOperator(t, 2) + ops[2] = initiator.Operator{srv2.srv.URL, 2, &srv2.privKey.PublicKey} + srv3 := CreateOperator(t, 3) + ops[3] = initiator.Operator{srv3.srv.URL, 3, &srv3.privKey.PublicKey} + srv4 := CreateOperator(t, 4) + ops[4] = initiator.Operator{srv4.srv.URL, 4, &srv4.privKey.PublicKey} + srv5 := CreateOperator(t, 5) + ops[5] = initiator.Operator{srv5.srv.URL, 5, &srv5.privKey.PublicKey} + srv6 := CreateOperator(t, 6) + ops[6] = initiator.Operator{srv6.srv.URL, 6, &srv6.privKey.PublicKey} + srv7 := CreateOperator(t, 7) + ops[7] = initiator.Operator{srv7.srv.URL, 7, &srv7.privKey.PublicKey} + // Initiator priv key + _, pv, err := rsaencryption.GenerateKeys() + require.NoError(t, err) + priv, err := rsaencryption.ConvertPemToPrivateKey(string(pv)) + clnt := initiator.New(priv, ops) + withdraw := newEthAddress(t) + owner := newEthAddress(t) + depositData, ks, err := clnt.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) + require.NoError(t, err) + sharesDataSigned, err := hex.DecodeString(ks.Payload.Readable.Shares[2:]) + require.NoError(t, err) + pubkeyraw, err := hex.DecodeString(ks.Payload.Readable.PublicKey[2:]) + require.NoError(t, err) + testSharesData(t, ops, []*rsa.PrivateKey{srv1.privKey, srv2.privKey, srv3.privKey, srv4.privKey, srv5.privKey, srv6.privKey, srv7.privKey}, sharesDataSigned, pubkeyraw, owner, 0) + testDepositData(t, depositData, withdraw.Bytes(), owner, 0) + srv1.srv.Close() + srv2.srv.Close() + srv3.srv.Close() + srv4.srv.Close() + srv5.srv.Close() + srv6.srv.Close() + srv7.srv.Close() + }) + t.Run("test 12 operators happy flow", func(t *testing.T) { + ops := make(map[uint64]initiator.Operator) + srv1 := CreateOperator(t, 1) + ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} + srv2 := CreateOperator(t, 2) + ops[2] = initiator.Operator{srv2.srv.URL, 2, &srv2.privKey.PublicKey} + srv3 := CreateOperator(t, 3) + ops[3] = initiator.Operator{srv3.srv.URL, 3, &srv3.privKey.PublicKey} + srv4 := CreateOperator(t, 4) + ops[4] = initiator.Operator{srv4.srv.URL, 4, &srv4.privKey.PublicKey} + srv5 := CreateOperator(t, 5) + ops[5] = initiator.Operator{srv5.srv.URL, 5, &srv5.privKey.PublicKey} + srv6 := CreateOperator(t, 6) + ops[6] = initiator.Operator{srv6.srv.URL, 6, &srv6.privKey.PublicKey} + srv7 := CreateOperator(t, 7) + ops[7] = initiator.Operator{srv7.srv.URL, 7, &srv7.privKey.PublicKey} + srv8 := CreateOperator(t, 8) + ops[8] = initiator.Operator{srv8.srv.URL, 8, &srv8.privKey.PublicKey} + srv9 := CreateOperator(t, 9) + ops[9] = initiator.Operator{srv9.srv.URL, 9, &srv9.privKey.PublicKey} + srv10 := CreateOperator(t, 10) + ops[10] = initiator.Operator{srv10.srv.URL, 10, &srv10.privKey.PublicKey} + srv11 := CreateOperator(t, 11) + ops[11] = initiator.Operator{srv11.srv.URL, 11, &srv11.privKey.PublicKey} + srv12 := CreateOperator(t, 12) + ops[12] = initiator.Operator{srv12.srv.URL, 12, &srv12.privKey.PublicKey} + // Initiator priv key + _, pv, err := rsaencryption.GenerateKeys() + require.NoError(t, err) + priv, err := rsaencryption.ConvertPemToPrivateKey(string(pv)) + clnt := initiator.New(priv, ops) + withdraw := newEthAddress(t) + owner := newEthAddress(t) + depositData, ks, err := clnt.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) + require.NoError(t, err) + sharesDataSigned, err := hex.DecodeString(ks.Payload.Readable.Shares[2:]) + require.NoError(t, err) + pubkeyraw, err := hex.DecodeString(ks.Payload.Readable.PublicKey[2:]) + require.NoError(t, err) + testSharesData(t, ops, []*rsa.PrivateKey{srv1.privKey, srv2.privKey, srv3.privKey, srv4.privKey, srv5.privKey, srv6.privKey, srv7.privKey, srv8.privKey, srv9.privKey, srv10.privKey, srv11.privKey, srv12.privKey}, sharesDataSigned, pubkeyraw, owner, 0) + testDepositData(t, depositData, withdraw.Bytes(), owner, 0) + srv1.srv.Close() + srv2.srv.Close() + srv3.srv.Close() + srv4.srv.Close() + srv5.srv.Close() + srv6.srv.Close() + srv7.srv.Close() + srv8.srv.Close() + srv9.srv.Close() + srv10.srv.Close() + srv11.srv.Close() + srv12.srv.Close() + }) } func testSharesData(t *testing.T, ops map[uint64]initiator.Operator, keys []*rsa.PrivateKey, sharesData []byte, validatorPublicKey []byte, owner common.Address, nonce uint16) { diff --git a/pkgs/dkg/drand.go b/pkgs/dkg/drand.go index e05a2a9d..9c5f5783 100644 --- a/pkgs/dkg/drand.go +++ b/pkgs/dkg/drand.go @@ -11,6 +11,10 @@ import ( "github.com/attestantio/go-eth2-client/spec/phase0" eth2_key_manager_core "github.com/bloxapp/eth2-key-manager/core" + "github.com/bloxapp/ssv-dkg/pkgs/board" + "github.com/bloxapp/ssv-dkg/pkgs/crypto" + "github.com/bloxapp/ssv-dkg/pkgs/utils" + "github.com/bloxapp/ssv-dkg/pkgs/wire" ssvspec_types "github.com/bloxapp/ssv-spec/types" "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/drand/kyber" @@ -23,11 +27,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/viper" - - "github.com/bloxapp/ssv-dkg/pkgs/board" - "github.com/bloxapp/ssv-dkg/pkgs/crypto" - "github.com/bloxapp/ssv-dkg/pkgs/utils" - "github.com/bloxapp/ssv-dkg/pkgs/wire" ) const ( diff --git a/pkgs/initiator/initiator_test.go b/pkgs/initiator/initiator_test.go index e7bc2998..bde07331 100644 --- a/pkgs/initiator/initiator_test.go +++ b/pkgs/initiator/initiator_test.go @@ -1,28 +1,37 @@ package initiator_test import ( + "bytes" "crypto/ecdsa" "crypto/rsa" + "encoding/hex" "fmt" "net/http/httptest" + "sync" "testing" "time" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/bloxapp/ssv/utils/rsaencryption" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" eth_crypto "github.com/ethereum/go-ethereum/crypto" "github.com/go-chi/chi/v5" + "github.com/herumi/bls-eth-go-binary/bls" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/bloxapp/ssv-dkg/pkgs/crypto" + ourcrypto "github.com/bloxapp/ssv-dkg/pkgs/crypto" + "github.com/bloxapp/ssv-dkg/pkgs/dkg" "github.com/bloxapp/ssv-dkg/pkgs/initiator" - mock_operator "github.com/bloxapp/ssv-dkg/pkgs/initiator/mock_operator" - "github.com/bloxapp/ssv-dkg/pkgs/initiator/mock_operator/dkg" "github.com/bloxapp/ssv-dkg/pkgs/load" operator "github.com/bloxapp/ssv-dkg/pkgs/operator" - "github.com/bloxapp/ssv/utils/rsaencryption" ) +const encryptedKeyLength = 256 + // TODO: use mocks instead of servers type testOperator struct { id uint64 @@ -57,9 +66,11 @@ const exmaplePath = "../../examples/" func TestOperatorMisbehave(t *testing.T) { ops := make(map[uint64]initiator.Operator) + srv1 := CreateTestOperator(t, 1) srv2 := CreateTestOperator(t, 2) srv3 := CreateTestOperator(t, 3) srv4 := CreateTestOperator(t, 4) + ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} ops[2] = initiator.Operator{srv2.srv.URL, 2, &srv2.privKey.PublicKey} ops[3] = initiator.Operator{srv3.srv.URL, 3, &srv3.privKey.PublicKey} ops[4] = initiator.Operator{srv4.srv.URL, 4, &srv4.privKey.PublicKey} @@ -67,94 +78,49 @@ func TestOperatorMisbehave(t *testing.T) { require.NoError(t, err) priv, err := rsaencryption.ConvertPemToPrivateKey(string(pv)) require.NoError(t, err) - t.Run("test wrong amount of opeators < 4", func(t *testing.T) { - opmap, err := load.LoadOperatorsJson([]byte(operatorsMetaData)) + withdraw := common.HexToAddress("0x0000000000000000000000000000000000000009") + owner := common.HexToAddress("0x0000000000000000000000000000000000000007") + t.Run("happy flow", func(t *testing.T) { + initiator := initiator.New(priv, ops) + depositData, keyshares, err := initiator.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) require.NoError(t, err) - clnt := initiator.New(priv, opmap) - _, _, err = clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) + testSharesData(t, ops, []*rsa.PrivateKey{srv1.privKey, srv2.privKey, srv3.privKey, srv4.privKey}, keyshares, owner, 0) + testDepositData(t, depositData, withdraw.Bytes(), owner, 0) + }) + t.Run("test wrong amount of opeators < 4", func(t *testing.T) { + initiator := initiator.New(priv, ops) + _, _, err = initiator.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) require.ErrorContains(t, err, "minimum supported amount of operators is 4") }) t.Run("test wrong amount of opeators > 13", func(t *testing.T) { - opmap, err := load.LoadOperatorsJson([]byte(operatorsMetaData)) - require.NoError(t, err) - clnt := initiator.New(priv, opmap) - _, _, err = clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) + initiator := initiator.New(priv, ops) + _, _, err = initiator.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) require.ErrorContains(t, err, "maximum supported amount of operators is 13") }) t.Run("test opeators not unique", func(t *testing.T) { - opmap, err := load.LoadOperatorsJson([]byte(operatorsMetaData)) - require.NoError(t, err) - clnt := initiator.New(priv, opmap) - _, _, err = clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7, 7, 9, 10, 11, 12, 12}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) + initiator := initiator.New(priv, ops) + _, _, err = initiator.StartDKG(withdraw.Bytes(), []uint64{1, 2, 3, 4, 5, 6, 7, 7, 9, 10, 11, 12, 12}, [4]byte{0, 0, 0, 0}, "mainnnet", owner, 0) require.ErrorContains(t, err, "operators ids should be unique in the list") }) - t.Run("test wrong server key", func(t *testing.T) { - srv1 := CreateTestOperatorRandomKey(t, 1) - ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv2.privKey.PublicKey} - clnt := initiator.New(priv, ops) - _, _, err := clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) - require.ErrorContains(t, err, "my operator is missing inside the op list") - srv1.srv.Close() - }) - - t.Run("test wrong partial deposit signature", func(t *testing.T) { - eveMsg := dkg.EveTest{ - WrongPartialSig: "0x87912f24669427628885cf0b70385b94694951626805ff565f4d2a0b74c433a45b279769ff23c23c8dd4ae3625fa06c20df368c0dc24931f3ebe133b3e1fed7d3477c51fa291e61052b0286c7fc453bb5e10346c43eadda9ef1bac8db14acda4", - } - srv1 := CreateEveTestOperator(t, 1, &eveMsg) - ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} - clnt := initiator.New(priv, ops) - _, _, err := clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) - require.ErrorContains(t, err, "error verifying partial deposit signature") - srv1.srv.Close() - }) - t.Run("test wrong request ID", func(t *testing.T) { - eveMsg := dkg.EveTest{ - WrongID: "0x0000000000000000630ab8af69364a6db7b6d7d59bb60f23", - } - srv1 := CreateEveTestOperator(t, 1, &eveMsg) - ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} - clnt := initiator.New(priv, ops) - _, _, err := clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) - require.ErrorContains(t, err, "DKG result has wrong ID") - srv1.srv.Close() - }) - srv2.srv.Close() - srv3.srv.Close() - srv4.srv.Close() -} - -func TestTimeout(t *testing.T) { - ops := make(map[uint64]initiator.Operator) - eveMsg := dkg.EveTest{ - Timeout: time.Second * 30, - } - srv1 := CreateEveTestOperator(t, 1, &eveMsg) - srv2 := CreateTestOperator(t, 2) - srv3 := CreateTestOperator(t, 3) - srv4 := CreateTestOperator(t, 4) - ops[1] = initiator.Operator{srv1.srv.URL, 1, &srv1.privKey.PublicKey} - ops[2] = initiator.Operator{srv2.srv.URL, 2, &srv2.privKey.PublicKey} - ops[3] = initiator.Operator{srv3.srv.URL, 3, &srv3.privKey.PublicKey} - ops[4] = initiator.Operator{srv4.srv.URL, 4, &srv4.privKey.PublicKey} - _, pv, err := rsaencryption.GenerateKeys() - require.NoError(t, err) - priv, err := rsaencryption.ConvertPemToPrivateKey(string(pv)) - require.NoError(t, err) - clnt := initiator.New(priv, ops) - _, _, err = clnt.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4}, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) - require.ErrorContains(t, err, "Client.Timeout exceeded while awaiting headers") srv1.srv.Close() srv2.srv.Close() srv3.srv.Close() srv4.srv.Close() } + func CreateTestOperator(t *testing.T, id uint64) *testOperator { - priv, err := load.EncryptedPrivateKey(exmaplePath+"server"+fmt.Sprintf("%v", id)+"/encrypted_private_key.json", "12345678") + priv, err := load.EncryptedPrivateKey(exmaplePath+"operator"+fmt.Sprintf("%v", id)+"/encrypted_private_key.json", "12345678") require.NoError(t, err) r := chi.NewRouter() - swtch := operator.NewSwitch(priv) + swtch := &operator.Switch{ + Logger: logrus.NewEntry(logrus.New()), + Mtx: sync.RWMutex{}, + InstanceInitTime: make(map[operator.InstanceID]time.Time, operator.MaxInstances), + Instances: make(map[operator.InstanceID]operator.Instance, operator.MaxInstances), + PrivateKey: priv, + } + lg := logrus.New() lg.SetLevel(logrus.DebugLevel) s := &operator.Server{ @@ -171,46 +137,77 @@ func CreateTestOperator(t *testing.T, id uint64) *testOperator { } } -func CreateTestOperatorRandomKey(t *testing.T, id uint64) *testOperator { - priv, _, err := crypto.GenerateKeys() - require.NoError(t, err) - r := chi.NewRouter() - swtch := operator.NewSwitch(priv) - lg := logrus.New() - lg.SetLevel(logrus.DebugLevel) - s := &operator.Server{ - Logger: logrus.NewEntry(lg).WithField("comp", "server"), - Router: r, - State: swtch, +func testSharesData(t *testing.T, ops map[uint64]initiator.Operator, keys []*rsa.PrivateKey, ks *initiator.KeyShares, owner common.Address, nonce uint16) { + sharesData, err := hex.DecodeString(ks.Payload.Readable.Shares[2:]) + validatorPublicKey, err := hex.DecodeString(ks.Payload.Readable.PublicKey[2:]) + + operatorCount := len(keys) + signatureOffset := phase0.SignatureLength + pubKeysOffset := phase0.PublicKeyLength*operatorCount + signatureOffset + sharesExpectedLength := encryptedKeyLength*operatorCount + pubKeysOffset + require.Len(t, sharesData, sharesExpectedLength) + signature := sharesData[:signatureOffset] + msg := []byte("Hello") + require.NoError(t, ourcrypto.VerifyOwnerNoceSignature(signature, owner, validatorPublicKey, nonce)) + _ = splitBytes(sharesData[signatureOffset:pubKeysOffset], phase0.PublicKeyLength) + encryptedKeys := splitBytes(sharesData[pubKeysOffset:], len(sharesData[pubKeysOffset:])/operatorCount) + sigs2 := make(map[uint64][]byte) + for i, enck := range encryptedKeys { + priv := keys[i] + share, err := rsaencryption.DecodeKey(priv, enck) + require.NoError(t, err) + secret := &bls.SecretKey{} + require.NoError(t, secret.SetHexString(string(share))) + // Find operator ID by PubKey + var operatorID uint64 + for id, op := range ops { + if bytes.Equal(priv.PublicKey.N.Bytes(), op.PubKey.N.Bytes()) { + operatorID = id + } + } + sig := secret.SignByte(msg) + sigs2[operatorID] = sig.Serialize() } - operator.RegisterRoutes(s) - sTest := httptest.NewServer(s.Router) - return &testOperator{ - id: id, - privKey: priv, - srv: sTest, + recon, err := ReconstructSignatures(sigs2) + require.NoError(t, err) + require.NoError(t, VerifyReconstructedSignature(recon, validatorPublicKey, msg)) +} + +// ReconstructSignatures receives a map of user indexes and serialized bls.Sign. +// It then reconstructs the original threshold signature using lagrange interpolation +func ReconstructSignatures(signatures map[uint64][]byte) (*bls.Sign, error) { + reconstructedSig := bls.Sign{} + idVec := make([]bls.ID, 0) + sigVec := make([]bls.Sign, 0) + for index, signature := range signatures { + blsID := bls.ID{} + err := blsID.SetDecString(fmt.Sprintf("%d", index)) + if err != nil { + return nil, err + } + idVec = append(idVec, blsID) + blsSig := bls.Sign{} + + err = blsSig.Deserialize(signature) + if err != nil { + return nil, err + } + sigVec = append(sigVec, blsSig) } + err := reconstructedSig.Recover(sigVec, idVec) + return &reconstructedSig, err } -func CreateEveTestOperator(t *testing.T, id uint64, eveCase *dkg.EveTest) *testOperator { - priv, err := load.EncryptedPrivateKey(exmaplePath+"server"+fmt.Sprintf("%v", id)+"/encrypted_private_key.json", "12345678") - require.NoError(t, err) - r := chi.NewRouter() - swtch := mock_operator.NewSwitch(priv) - lg := logrus.New() - lg.SetLevel(logrus.DebugLevel) - s := &mock_operator.Server{ - Logger: logrus.NewEntry(lg).WithField("comp", "server"), - Router: r, - State: swtch, +func VerifyReconstructedSignature(sig *bls.Sign, validatorPubKey []byte, msg []byte) error { + pk := &bls.PublicKey{} + if err := pk.Deserialize(validatorPubKey); err != nil { + return errors.Wrap(err, "could not deserialize validator pk") } - mock_operator.RegisterRoutes(s, eveCase) - sTest := httptest.NewServer(s.Router) - return &testOperator{ - id: id, - privKey: priv, - srv: sTest, + // verify reconstructed sig + if res := sig.VerifyByte(pk, msg); !res { + return errors.New("could not reconstruct a valid signature") } + return nil } func newEthAddress(t *testing.T) common.Address { @@ -223,88 +220,43 @@ func newEthAddress(t *testing.T) common.Address { return address } -// func TestHappyFlowMock(t *testing.T) { -// logger := logrus.NewEntry(logrus.New()) - -// logger.Infof("Starting intg test") - -// srv1 := CreateTestOperator(t, 1) -// srv2 := CreateTestOperator(t, 2) -// srv3 := CreateTestOperator(t, 3) -// srv4 := CreateTestOperator(t, 4) - -// logger.Infof("Servers created") - -// eg := errgroup.Group{} -// eg.Go(func() error { -// err := srv1.Start(3030) -// require.NoError(t, err) -// return err -// }) -// eg.Go(func() error { -// err := srv2.Start(3031) -// require.NoError(t, err) -// return err -// }) -// eg.Go(func() error { -// err := srv3.Start(3032) -// require.NoError(t, err) -// return err -// }) -// eg.Go(func() error { -// err := srv4.Start(3033) -// require.NoError(t, err) -// return err -// }) - -// logger.Infof("Servers Started") - -// opmap, err := load.LoadOperatorsJson([]byte(operatorsMetaData)) -// require.NoError(t, err) - -// mockCtrl := gomock.NewController(t) -// defer mockCtrl.Finish() -// mockClient := mocks.NewMockDKGClient(mockCtrl) - -// c := req.C() -// // Set timeout for operator responses -// c.SetTimeout(30 * time.Second) -// client := &client.Client{ -// Logger: logrus.NewEntry(logrus.New()), -// Client: c, -// Operators: opmap, -// } - -// parts := make([]*wire.Operator, 0, 0) -// for _, id := range []uint64{1, 2, 3, 4} { -// op, ok := client.Operators[id] -// if !ok { -// t.FailNow() -// } -// pkBytes, err := crypto.EncodePublicKey(op.PubKey) -// require.NoError(t, err) -// parts = append(parts, &wire.Operator{ -// ID: op.ID, -// PubKey: pkBytes, -// }) -// } -// // Add messages verification coming form operators -// verify, err := client.CreateVerifyFunc(parts) -// require.NoError(t, err) -// client.VerifyFunc = verify +func splitBytes(buf []byte, lim int) [][]byte { + var chunk []byte + chunks := make([][]byte, 0, len(buf)/lim+1) + for len(buf) >= lim { + chunk, buf = buf[:lim], buf[lim:] + chunks = append(chunks, chunk) + } + if len(buf) > 0 { + chunks = append(chunks, buf[:]) + } + return chunks +} -// // make init message -// init := &wire.Init{ -// Operators: parts, -// T: 3, -// WithdrawalCredentials: common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), -// Fork: [4]byte{0, 0, 0, 0}, -// Owner: common.HexToAddress("0x0000000000000000000000000000000000000007"), -// Nonce: 0, -// } +func testDepositData(t *testing.T, depsitDataJson *initiator.DepositDataJson, withdrawCred []byte, owner common.Address, nonce uint16) { + require.True(t, bytes.Equal(crypto.WithdrawalCredentialsHash(withdrawCred), hexutil.MustDecode("0x"+depsitDataJson.WithdrawalCredentials))) + masterSig := &bls.Sign{} + require.NoError(t, masterSig.DeserializeHexStr(depsitDataJson.Signature)) + valdatorPubKey := &bls.PublicKey{} + require.NoError(t, valdatorPubKey.DeserializeHexStr(depsitDataJson.PubKey)) -// id := client.NewID() -// mockClient.EXPECT().SendInitMsg(init, id).Return(nil, fmt.Errorf("Test err")).Times(1) -// _, _, err = client.StartDKG(common.HexToAddress("0x0000000000000000000000000000000000000009").Bytes(), []uint64{1, 2, 3, 4}, 3, [4]byte{0, 0, 0, 0}, "mainnnet", common.HexToAddress("0x0000000000000000000000000000000000000007"), 0) -// require.NoError(t, err) -// } + // Check root + var fork [4]byte + copy(fork[:], hexutil.MustDecode("0x"+depsitDataJson.ForkVersion)) + depositDataRoot, err := ourcrypto.DepositDataRoot(withdrawCred, valdatorPubKey, dkg.GetNetworkByFork(fork), initiator.MaxEffectiveBalanceInGwei) + require.NoError(t, err) + res := masterSig.VerifyByte(valdatorPubKey, depositDataRoot[:]) + require.True(t, res) + depositData, _, err := ourcrypto.DepositData(masterSig.Serialize(), withdrawCred, valdatorPubKey.Serialize(), dkg.GetNetworkByFork(fork), initiator.MaxEffectiveBalanceInGwei) + require.NoError(t, err) + res, err = crypto.VerifyDepositData(depositData, dkg.GetNetworkByFork(fork)) + require.NoError(t, err) + require.True(t, res) + depositMsg := &phase0.DepositMessage{ + WithdrawalCredentials: depositData.WithdrawalCredentials, + Amount: initiator.MaxEffectiveBalanceInGwei, + } + copy(depositMsg.PublicKey[:], depositData.PublicKey[:]) + depositMsgRoot, _ := depositMsg.HashTreeRoot() + require.True(t, bytes.Equal(depositMsgRoot[:], hexutil.MustDecode("0x"+depsitDataJson.DepositMessageRoot))) +} diff --git a/pkgs/initiator/mock_operator/dkg/drand.go b/pkgs/initiator/mock_operator/dkg/drand.go deleted file mode 100644 index 66daa04a..00000000 --- a/pkgs/initiator/mock_operator/dkg/drand.go +++ /dev/null @@ -1,557 +0,0 @@ -package dkg - -import ( - "crypto/rsa" - "encoding/hex" - "encoding/json" - "fmt" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - eth2_key_manager_core "github.com/bloxapp/eth2-key-manager/core" - ssvspec_types "github.com/bloxapp/ssv-spec/types" - "github.com/drand/kyber" - "github.com/drand/kyber/pairing" - "github.com/drand/kyber/share/dkg" - "github.com/drand/kyber/util/random" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - eth_crypto "github.com/ethereum/go-ethereum/crypto" - "github.com/herumi/bls-eth-go-binary/bls" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - types "github.com/wealdtech/go-eth2-types/v2" - util "github.com/wealdtech/go-eth2-util" - - "github.com/bloxapp/ssv-dkg/pkgs/board" - "github.com/bloxapp/ssv-dkg/pkgs/crypto" - "github.com/bloxapp/ssv-dkg/pkgs/wire" -) - -type EveTest struct { - WrongPartialSig string - Timeout time.Duration - WrongID string -} - -const ( - // MaxEffectiveBalanceInGwei is the max effective balance - MaxEffectiveBalanceInGwei phase0.Gwei = 32000000000 - - // BLSWithdrawalPrefixByte is the BLS withdrawal prefix - BLSWithdrawalPrefixByte = byte(0) -) - -// IsSupportedDepositNetwork returns true if the given network is supported -var IsSupportedDepositNetwork = func(network eth2_key_manager_core.Network) bool { - return network == eth2_key_manager_core.PyrmontNetwork || network == eth2_key_manager_core.PraterNetwork || network == eth2_key_manager_core.MainNetwork -} - -type Operator struct { - IP string - ID uint64 - Pubkey *rsa.PublicKey -} - -type DKGData struct { - ReqID [24]byte - init *wire.Init - Secret kyber.Scalar -} - -// Result is the last message in every DKG which marks a specific node's end of process -type Result struct { - // Operator ID - OperatorID uint32 - // Operator RSA pubkey - PubKeyRSA *rsa.PublicKey - // RequestID for the DKG instance (not used for signing) - RequestID [24]byte - // EncryptedShare standard SSV encrypted shares - EncryptedShare []byte - // SharePubKey is the share's BLS pubkey - SharePubKey []byte - // ValidatorPubKey the resulting public key corresponding to the shared private key - ValidatorPubKey []byte - // Partial Operator Signature of Deposit Data - DepositPartialSignature []byte - // DepositPartialSignature index - DepositPartialSignatureIndex uint64 - // SSV owner + nonce signature - OwnerNoncePartialSignature []byte -} - -// Encode returns a msg encoded bytes or error -func (msg *Result) Encode() ([]byte, error) { - return json.Marshal(msg) -} - -// Decode returns error if decoding failed -func (msg *Result) Decode(data []byte) error { - return json.Unmarshal(data, msg) -} - -var ErrAlreadyExists = errors.New("duplicate message") - -type LocalOwner struct { - Logger *logrus.Entry - startedDKG chan struct{} - ErrorChan chan error - ID uint64 - data *DKGData - b *board.Board - suite pairing.Suite - BroadcastF func([]byte) error - Exchanges map[uint64]*wire.Exchange - OpPrivKey *rsa.PrivateKey - SecretShare *dkg.DistKeyShare - - VerifyFunc func(id uint64, msg, sig []byte) error - SignFunc func([]byte) ([]byte, error) - - Owner common.Address - Nonce uint64 - done chan struct{} -} - -type OwnerOpts struct { - Logger *logrus.Entry - ID uint64 - BroadcastF func([]byte) error - Suite pairing.Suite - VerifyFunc func(id uint64, msg, sig []byte) error - SignFunc func([]byte) ([]byte, error) - OpPrivKey *rsa.PrivateKey - Owner [20]byte - Nonce uint64 -} - -func New(opts OwnerOpts) *LocalOwner { - owner := &LocalOwner{ - Logger: opts.Logger, - startedDKG: make(chan struct{}, 1), - ErrorChan: make(chan error, 1), - ID: opts.ID, - BroadcastF: opts.BroadcastF, - Exchanges: make(map[uint64]*wire.Exchange), - SignFunc: opts.SignFunc, - VerifyFunc: opts.VerifyFunc, - done: make(chan struct{}, 1), - suite: opts.Suite, - OpPrivKey: opts.OpPrivKey, - Owner: opts.Owner, - Nonce: opts.Nonce, - } - return owner -} - -func (o *LocalOwner) StartDKG(eve *EveTest) error { - o.Logger.Infof("Starting DKG") - nodes := make([]dkg.Node, 0) - for id, e := range o.Exchanges { - p := o.suite.G1().Point() - if err := p.UnmarshalBinary(e.PK); err != nil { - return err - } - - nodes = append(nodes, dkg.Node{ - Index: dkg.Index(id - 1), - Public: p, - }) - } - - // New protocol - p, err := wire.NewDKGProtocol(&wire.Config{ - Identifier: o.data.ReqID[:], - Secret: o.data.Secret, - Nodes: nodes, - Suite: o.suite, - T: int(o.data.init.T), - Board: o.b, - - Logger: o.Logger, - }) - if err != nil { - return err - } - - go func(p *dkg.Protocol, postF func(res *dkg.OptionResult, eve *EveTest) error, eve *EveTest) { - res := <-p.WaitEnd() - postF(&res, eve) - }(p, o.PostDKG, eve) - close(o.startedDKG) - return nil -} - -func (o *LocalOwner) Broadcast(ts *wire.Transport) error { - bts, err := ts.MarshalSSZ() - if err != nil { - return err - } - // Sign message with RSA private key - sign, err := o.SignFunc(bts) - if err != nil { - return err - } - - signed := &wire.SignedTransport{ - Message: ts, - Signer: o.ID, - Signature: sign, - } - - final, err := signed.MarshalSSZ() - if err != nil { - return err - } - - o.Logger.Debugf("responding with a signed message, msg:%v", hex.EncodeToString(final)) - - return o.BroadcastF(final) -} - -func (o *LocalOwner) PostDKG(res *dkg.OptionResult, eve *EveTest) error { - o.Logger.Infof("<<<< ---- DKG Result ---- >>>>") - o.Logger.Debugf("DKG PROTOCOL RESULT %v", res.Result) - if res.Error != nil { - o.Logger.Error(res.Error) - o.broadcastError(res.Error) - return res.Error - } - // Store result share a instance - // TODO: store DKG result at instance for now just as global variable - o.SecretShare = res.Result.Key - - // Get validator BLS public key from result - validatorPubKey, err := crypto.ResultToValidatorPK(res.Result, o.suite.G1().(dkg.Suite)) - if err != nil { - o.broadcastError(err) - return err - } - o.Logger.Debugf("Validator public key %x", validatorPubKey.Serialize()) - - // Get BLS partial secret key share from DKG - secretKeyBLS, err := crypto.ResultToShareSecretKey(res.Result) - if err != nil { - o.broadcastError(err) - return err - } - // Get BLS partial secret key index. We add 1 because DKG share index starts from 0 but BLS aggregation expects it from 1 - secretKeyBLSindex := res.Result.Key.Share.I + 1 - // Encrypt BLS share for SSV contract - encryptedShare, err := crypto.Encrypt(&o.OpPrivKey.PublicKey, []byte("0x"+secretKeyBLS.GetHexString())) - if err != nil { - o.broadcastError(err) - return err - } - o.Logger.Debugf("Encrypted share %x", encryptedShare) - o.Logger.Debugf("Withdrawal Credentials %x", o.data.init.WithdrawalCredentials) - o.Logger.Debugf("Fork Version %x", o.data.init.Fork) - o.Logger.Debugf("Domain %x", ssvspec_types.DomainDeposit) - - // Sign root - depositRootSig, signRoot, err := SignDepositData(secretKeyBLS, o.data.init.WithdrawalCredentials, validatorPubKey, getNetworkByFork(o.data.init.Fork), MaxEffectiveBalanceInGwei) - o.Logger.Debugf("Root %x", signRoot) - o.Logger.Infof("Partial sig %x", depositRootSig.Serialize()) - // Validate partial signature - val := depositRootSig.VerifyByte(secretKeyBLS.GetPublicKey(), signRoot) - if !val { - o.broadcastError(err) - return fmt.Errorf("partial deposit root signature isnt valid %x", depositRootSig.Serialize()) - } - // Sign SSV owner + nonce - data := []byte(fmt.Sprintf("%s:%d", o.Owner.String(), o.Nonce)) - hash := eth_crypto.Keccak256([]byte(data)) - o.Logger.Debugf("Owner, Nonce %x, %d", o.Owner, o.Nonce) - o.Logger.Debugf("SSV Keccak 256 of Owner + Nonce %x", hash) - sigOwnerNonce := secretKeyBLS.SignByte(hash) - if err != nil { - o.broadcastError(err) - return err - } - // Verify partial SSV owner + nonce signature - val = sigOwnerNonce.VerifyByte(secretKeyBLS.GetPublicKey(), hash) - if !val { - o.broadcastError(err) - return fmt.Errorf("partial owner + nonce signature isnt valid %x", sigOwnerNonce.Serialize()) - } - o.Logger.Debugf("SSV owner + nonce signature %x", sigOwnerNonce.Serialize()) - if eve != nil { - switch { - case eve.WrongPartialSig != "": - depositRootSig.SetHexString(eve.WrongPartialSig) - case eve.Timeout != 0: - time.Sleep(eve.Timeout) - case eve.WrongID != "": - copy(o.data.ReqID[:], hexutil.MustDecode(eve.WrongID)) - } - - } - out := Result{ - RequestID: o.data.ReqID, - EncryptedShare: encryptedShare, - SharePubKey: secretKeyBLS.GetPublicKey().Serialize(), - ValidatorPubKey: validatorPubKey.Serialize(), - DepositPartialSignature: depositRootSig.Serialize(), - DepositPartialSignatureIndex: uint64(secretKeyBLSindex), - PubKeyRSA: &o.OpPrivKey.PublicKey, - OperatorID: uint32(o.ID), - OwnerNoncePartialSignature: sigOwnerNonce.Serialize(), - } - - encodedOutput, err := out.Encode() - if err != nil { - o.broadcastError(err) - return err - } - - tsMsg := &wire.Transport{ - Type: wire.OutputMessageType, - Identifier: o.data.ReqID, - Data: encodedOutput, - } - - o.Broadcast(tsMsg) - close(o.done) - return nil -} - -func (o *LocalOwner) Init(reqID [24]byte, init *wire.Init) (*wire.Transport, error) { - if o.data == nil { - o.data = &DKGData{} - } - o.data.init = init - o.data.ReqID = reqID - kyberLogger := logrus.NewEntry(logrus.New()) - kyberLogger = kyberLogger.WithField("reqid", o.data.ReqID) - o.b = board.NewBoard( - kyberLogger, - func(msg *wire.KyberMessage) error { - kyberLogger.Logger.Infof("Server: broadcasting kyber message") - - byts, err := msg.MarshalSSZ() - if err != nil { - return err - } - - trsp := &wire.Transport{ - Type: wire.KyberMessageType, - Identifier: o.data.ReqID, - Data: byts, - } - - // todo not loop with channels - go func(trsp *wire.Transport) { - if err := o.Broadcast(trsp); err != nil { - o.Logger.Errorf("broadcasting failed %v", err) - } - }(trsp) - - return nil - }, - ) - - eciesSK, pk := InitSecret(o.suite) - o.data.Secret = eciesSK - bts, _, err := CreateExchange(pk) - if err != nil { - return nil, err - } - return ExchangeWireMessage(bts, reqID), nil -} - -func (o *LocalOwner) processDKG(from uint64, msg *wire.Transport) error { - kyberMsg := &wire.KyberMessage{} - if err := kyberMsg.UnmarshalSSZ(msg.Data); err != nil { - return err - } - - o.Logger.Infof("operator: Recieved kyber msg of type %v, from %v", kyberMsg.Type.String(), from) - - switch kyberMsg.Type { - case wire.KyberDealBundleMessageType: - b, err := wire.DecodeDealBundle(kyberMsg.Data, o.suite.G1().(dkg.Suite)) - if err != nil { - return err - } - - o.Logger.Infof("operator: received deal bundle from %d", from) - - o.b.DealC <- *b - - o.Logger.Infof("operator: gone through deal sending %d", from) - - case wire.KyberResponseBundleMessageType: - - b, err := wire.DecodeResponseBundle(kyberMsg.Data) - if err != nil { - return err - } - - o.Logger.Infof("operator: received response bundle from %d", from) - - o.b.ResponseC <- *b - case wire.KyberJustificationBundleMessageType: - b, err := wire.DecodeJustificationBundle(kyberMsg.Data, o.suite.G1().(dkg.Suite)) - if err != nil { - return err - } - - o.Logger.Infof("operator: received justification bundle from %d", from) - - o.b.JustificationC <- *b - default: - return errors.New("unknown kyber message type") - } - return nil -} - -func (o *LocalOwner) Process(from uint64, st *wire.SignedTransport, eve *EveTest) error { - - msgbts, err := st.Message.MarshalSSZ() - if err != nil { - return err - } - // Verify operator signatures - if err := o.VerifyFunc(st.Signer, msgbts, st.Signature); err != nil { - return err - } - - t := st.Message - - o.Logger.Infof("operator: got msg from type %s, at: %d", t.Type.String(), o.ID) - - switch t.Type { - case wire.ExchangeMessageType: - exchMsg := &wire.Exchange{} - if err := exchMsg.UnmarshalSSZ(t.Data); err != nil { - return err - } - if _, ok := o.Exchanges[from]; ok { - return ErrAlreadyExists - } - - o.Exchanges[from] = exchMsg - - if len(o.Exchanges) == len(o.data.init.Operators) { - if err := o.StartDKG(eve); err != nil { - return err - } - } - case wire.KyberMessageType: - <-o.startedDKG - return o.processDKG(from, t) - default: - return errors.New("unknown type") - } - - return nil -} - -func InitSecret(suite pairing.Suite) (kyber.Scalar, kyber.Point) { - eciesSK := suite.G1().Scalar().Pick(random.New()) - pk := suite.G1().Point().Mul(eciesSK, nil) - return eciesSK, pk -} - -func CreateExchange(pk kyber.Point) ([]byte, *wire.Exchange, error) { - pkByts, err := pk.MarshalBinary() - if err != nil { - return nil, nil, err - } - exch := wire.Exchange{ - PK: pkByts, - } - exchByts, err := exch.MarshalSSZ() - if err != nil { - return nil, nil, err - } - - return exchByts, &exch, nil -} - -func ExchangeWireMessage(exchData []byte, reqID [24]byte) *wire.Transport { - return &wire.Transport{ - Type: wire.ExchangeMessageType, - Identifier: reqID, - Data: exchData, - } -} - -func SignDepositData(validationKey *bls.SecretKey, withdrawalPubKey []byte, validatorPublicKey *bls.PublicKey, network eth2_key_manager_core.Network, amount phase0.Gwei) (*bls.Sign, []byte, error) { - if !IsSupportedDepositNetwork(network) { - return nil, nil, errors.Errorf("Network %s is not supported", network) - } - - depositMessage := &phase0.DepositMessage{ - WithdrawalCredentials: withdrawalCredentialsHash(withdrawalPubKey), - Amount: amount, - } - copy(depositMessage.PublicKey[:], validatorPublicKey.Serialize()) - - objRoot, err := depositMessage.HashTreeRoot() - if err != nil { - return nil, nil, errors.Wrap(err, "failed to determine the root hash of deposit data") - } - - // Compute domain - genesisForkVersion := network.GenesisForkVersion() - domain, err := types.ComputeDomain(types.DomainDeposit, genesisForkVersion[:], types.ZeroGenesisValidatorsRoot) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to calculate domain") - } - - signingData := phase0.SigningData{ - ObjectRoot: objRoot, - } - copy(signingData.Domain[:], domain[:]) - - root, err := signingData.HashTreeRoot() - if err != nil { - return nil, nil, errors.Wrap(err, "failed to determine the root hash of signing container") - } - - // Sign - sig := validationKey.SignByte(root[:]) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to sign the root") - } - return sig, root[:], nil -} - -// withdrawalCredentialsHash forms a 32 byte hash of the withdrawal public -// address. -// -// The specification is as follows: -// -// withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE -// withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:] -// -// where withdrawal_credentials is of type bytes32. -func withdrawalCredentialsHash(withdrawalPubKey []byte) []byte { - h := util.SHA256(withdrawalPubKey) - return append([]byte{BLSWithdrawalPrefixByte}, h[1:]...)[:32] -} - -func getNetworkByFork(fork [4]byte) eth2_key_manager_core.Network { - switch fork { - case [4]byte{0x00, 0x00, 0x10, 0x20}: - return eth2_key_manager_core.PraterNetwork - case [4]byte{0, 0, 0, 0}: - return eth2_key_manager_core.MainNetwork - default: - return eth2_key_manager_core.MainNetwork - } -} - -func (o *LocalOwner) broadcastError(err error) { - errMsgEnc, _ := json.Marshal(err.Error()) - errMsg := &wire.Transport{ - Type: wire.ErrorMessageType, - Identifier: o.data.ReqID, - Data: errMsgEnc, - } - - o.Broadcast(errMsg) - close(o.done) -} diff --git a/pkgs/initiator/mock_operator/mock_operator.go b/pkgs/initiator/mock_operator/mock_operator.go deleted file mode 100644 index baea25c9..00000000 --- a/pkgs/initiator/mock_operator/mock_operator.go +++ /dev/null @@ -1,114 +0,0 @@ -package mock_operator - -import ( - "crypto/rsa" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - - "github.com/bloxapp/ssv-dkg/pkgs/initiator/mock_operator/dkg" - "github.com/bloxapp/ssv-dkg/pkgs/wire" - ssvspec_types "github.com/bloxapp/ssv-spec/types" - "github.com/go-chi/chi/v5" - "github.com/sirupsen/logrus" -) - -type Server struct { - Logger *logrus.Entry - HttpServer *http.Server - Router chi.Router - State *Switch -} - -type KeySign struct { - ValidatorPK ssvspec_types.ValidatorPK - SigningRoot []byte -} - -// Encode returns a msg encoded bytes or error -func (msg *KeySign) Encode() ([]byte, error) { - return json.Marshal(msg) -} - -// Decode returns error if decoding failed -func (msg *KeySign) Decode(data []byte) error { - return json.Unmarshal(data, msg) -} - -func RegisterRoutes(s *Server, eve *dkg.EveTest) { - s.Router.Post("/init", func(writer http.ResponseWriter, request *http.Request) { - s.Logger.Info("Received init msg") - rawdata, _ := io.ReadAll(request.Body) - tr := &wire.Transport{} - if err := tr.UnmarshalSSZ(rawdata); err != nil { - s.Logger.Errorf("parsing failed, err %v", err) - writer.WriteHeader(http.StatusBadRequest) - writer.Write(wire.MakeErr(err)) - return - } - // Validate that incoming message is an init message - if tr.Type != wire.InitMessageType { - s.Logger.Errorf("non init message send to init route") - writer.WriteHeader(http.StatusBadRequest) - writer.Write(wire.MakeErr(errors.New("not init message to init route"))) - return - } - - reqid := tr.Identifier - - logger := s.Logger.WithField("reqid", hex.EncodeToString(reqid[:])) - logger.Infof("Initiating instance with init data") - b, err := s.State.InitInstance(reqid, tr.Data) - if err != nil { - logger.Errorf("failed to initiate instance err:%v", err) - - writer.WriteHeader(http.StatusBadRequest) - writer.Write(wire.MakeErr(err)) - return - } - writer.WriteHeader(http.StatusOK) - writer.Write(b) - }) - - s.Router.Post("/dkg", func(writer http.ResponseWriter, request *http.Request) { - s.Logger.Info("Received a dkg protocol message") - - rawdata, err := io.ReadAll(request.Body) - b, err := s.State.ProcessMessage(rawdata, eve) - if err != nil { - writer.WriteHeader(http.StatusBadRequest) - writer.Write(wire.MakeErr(err)) - return - } - writer.WriteHeader(http.StatusOK) - writer.Write(b) - }) -} - -func New(key *rsa.PrivateKey, eve *dkg.EveTest) *Server { - r := chi.NewRouter() - swtch := NewSwitch(key) - lg := logrus.New() - lg.SetLevel(logrus.DebugLevel) - s := &Server{ - Logger: logrus.NewEntry(lg).WithField("comp", "server"), - Router: r, - State: swtch, - } - RegisterRoutes(s, eve) - return s -} - -func (s *Server) Start(port uint16) error { - s.Logger.Infof("Server listening for incoming requests on port %d", port) - srv := &http.Server{Addr: fmt.Sprintf(":%v", port), Handler: s.Router} - s.HttpServer = srv - return s.HttpServer.ListenAndServe() -} - -func (s *Server) Stop() error { - return s.HttpServer.Close() -} diff --git a/pkgs/initiator/mock_operator/state.go b/pkgs/initiator/mock_operator/state.go deleted file mode 100644 index b43dce67..00000000 --- a/pkgs/initiator/mock_operator/state.go +++ /dev/null @@ -1,282 +0,0 @@ -package mock_operator - -import ( - "bytes" - "crypto/rsa" - "encoding/hex" - "errors" - "sync" - "time" - - "github.com/bloxapp/ssv-dkg/pkgs/crypto" - "github.com/bloxapp/ssv-dkg/pkgs/initiator/mock_operator/dkg" - "github.com/bloxapp/ssv-dkg/pkgs/wire" - bls "github.com/drand/kyber-bls12381" - "github.com/sirupsen/logrus" -) - -const MaxInstances = 1024 -const MaxInstanceTime = 5 * time.Minute - -var ErrMissingInstance = errors.New("got message to instance that I don't have, send Init first") -var ErrAlreadyExists = errors.New("got init msg for existing instance") -var ErrMaxInstances = errors.New("max number of instances ongoing, please wait") - -type Instance interface { - Process(uint64, *wire.SignedTransport, *dkg.EveTest) error // maybe return resp, threadsafe - ReadResponse() []byte - ReadError() error -} - -type instWrapper struct { - *dkg.LocalOwner - respChan chan []byte - errChan chan error -} - -func (iw *instWrapper) ReadResponse() []byte { - return <-iw.respChan -} -func (iw *instWrapper) ReadError() error { - return <-iw.errChan -} - -type InstanceID [24]byte - -func (s *Switch) CreateInstance(reqID [24]byte, init *wire.Init) (Instance, []byte, error) { - - verify, err := s.CreateVerifyFunc(init.Operators) - if err != nil { - return nil, nil, err - } - - serverID := uint64(0) - serverPubKey := s.privateKey.Public().(*rsa.PublicKey) - pkBytes, err := crypto.EncodePublicKey(serverPubKey) - if err != nil { - return nil, nil, err - } - for _, op := range init.Operators { - if bytes.Equal(op.PubKey, pkBytes) { - serverID = op.ID - break - } - } - - if serverID == 0 { - return nil, nil, errors.New("my operator is missing inside the op list") - } - - bchan := make(chan []byte, 1) - - broadcast := func(msg []byte) error { - bchan <- msg - return nil - } - - opts := dkg.OwnerOpts{ - Logger: s.logger.WithField("instance", hex.EncodeToString(reqID[:])), - BroadcastF: broadcast, - SignFunc: s.Sign, - VerifyFunc: verify, - Suite: bls.NewBLS12381Suite(), - ID: serverID, - OpPrivKey: s.privateKey, - Owner: init.Owner, - Nonce: init.Nonce, - } - owner := dkg.New(opts) - // wait for exchange msg - resp, err := owner.Init(reqID, init) - if err != nil { - return nil, nil, err - } - if err := owner.Broadcast(resp); err != nil { - return nil, nil, err - } - s.logger.Infof("Waiting for owner response to init") - res := <-bchan - return &instWrapper{owner, bchan, owner.ErrorChan}, res, nil -} - -func (s *Switch) Sign(msg []byte) ([]byte, error) { - return crypto.SignRSA(s.privateKey, msg) -} - -func (s *Switch) CreateVerifyFunc(ops []*wire.Operator) (func(id uint64, msg []byte, sig []byte) error, error) { - - inst_ops := make(map[uint64]*rsa.PublicKey) - for _, op := range ops { - pk, err := crypto.ParseRSAPubkey(op.PubKey) - if err != nil { - return nil, err - } - inst_ops[op.ID] = pk - } - return func(id uint64, msg []byte, sig []byte) error { - pk, ok := inst_ops[id] - if !ok { - return errors.New("ops not exist for this instance") - } - return crypto.VerifyRSA(pk, msg, sig) - }, nil -} - -type Switch struct { - logger *logrus.Entry - mtx sync.RWMutex - instanceInitTime map[InstanceID]time.Time - instances map[InstanceID]Instance - - privateKey *rsa.PrivateKey - - //broadcastF func([]byte) error -} - -func NewSwitch(pv *rsa.PrivateKey) *Switch { - return &Switch{ - logger: logrus.NewEntry(logrus.New()), - mtx: sync.RWMutex{}, - instanceInitTime: make(map[InstanceID]time.Time, MaxInstances), - instances: make(map[InstanceID]Instance, MaxInstances), - privateKey: pv, - } -} - -// TODO: does this improve anything over just locking everything? -//func (s *Switch) InitInstance(reqID InstanceID, init []byte) ([]byte, error) { -// -// s.mtx.RLock() -// l := len(s.instances) -// if l <= MaxInstances { -// s.mtx.Lock() -// cleaned := s.cleanInstances() -// if l-cleaned <= MaxInstances { -// s.mtx.RUnlock() -// s.mtx.Unlock() -// return nil, ErrMaxInstances -// } -// } -// _, ok := s.instances[init.ReqID] -// if ok { -// tm := s.instanceInitTime[init.ReqID] -// if !time.Now().After(tm.Add(MaxInstanceTime)) { -// s.mtx.RUnlock() -// return nil, ErrAlreadyExists -// } -// s.mtx.Lock() -// delete(s.instances, init.ReqID) -// delete(s.instanceInitTime, init.ReqID) -// s.mtx.Unlock() -// s.mtx.RUnlock() -// } -// inst, err := CreateInstance(init) // long action? if not maybe put inside mutex to reduce lock complexity? -// if err != nil { -// return nil, err -// } -// s.mtx.Lock() -// _, ok = s.instances[init.ReqID] -// if ok { -// s.mtx.RUnlock() -// s.mtx.Unlock() -// return nil, ErrAlreadyExists // created before us? -// } -// s.instances[init.ReqID] = inst -// s.mtx.RUnlock() -// s.mtx.Unlock() -// -// // TODO: get some ret from inst -// return inst.Start() -// -//} - -func (s *Switch) InitInstance(reqID [24]byte, initmsg []byte) ([]byte, error) { - logger := s.logger.WithField("reqid", hex.EncodeToString(reqID[:])) - logger.Infof("Got an init message") - init := &wire.Init{} - if err := init.UnmarshalSSZ(initmsg); err != nil { - return nil, err - } - - s.logger.Infof("decoded init message") - - s.mtx.Lock() - l := len(s.instances) - if l >= MaxInstances { - cleaned := s.cleanInstances() // not thread safe - if l-cleaned <= MaxInstances { - s.mtx.Unlock() - return nil, ErrMaxInstances - } - } - _, ok := s.instances[reqID] - if ok { - tm := s.instanceInitTime[reqID] - if !time.Now().After(tm.Add(MaxInstanceTime)) { - s.mtx.Unlock() - return nil, ErrAlreadyExists - } - delete(s.instances, reqID) - delete(s.instanceInitTime, reqID) - } - s.mtx.Unlock() - inst, resp, err := s.CreateInstance(reqID, init) // long action? if not maybe put inside mutex to reduce lock complexity? - - logger.Infof("Created instance") - - if err != nil { - return nil, err - } - s.mtx.Lock() - _, ok = s.instances[reqID] - if ok { - s.mtx.Unlock() - return nil, ErrAlreadyExists // created before us? - } - s.instances[reqID] = inst - s.mtx.Unlock() - - return resp, nil - -} - -func (s *Switch) cleanInstances() int { - count := 0 - for id, instime := range s.instanceInitTime { - if time.Now().After(instime.Add(MaxInstanceTime)) { - delete(s.instances, id) - delete(s.instanceInitTime, id) - count++ - } - } - return count -} - -func (s *Switch) ProcessMessage(dkgMsg []byte, eve *dkg.EveTest) ([]byte, error) { - // get instanceID - st := &wire.MultipleSignedTransports{} - err := st.UnmarshalSSZ(dkgMsg) - if err != nil { - return nil, err - } - - id := InstanceID(st.Identifier) - - s.mtx.RLock() - inst, ok := s.instances[id] - s.mtx.RUnlock() - - if !ok { - return nil, ErrMissingInstance - } - - for _, ts := range st.Messages { - err = inst.Process(ts.Signer, ts, eve) - if err != nil { - return nil, err - } - } - resp := inst.ReadResponse() - - return resp, nil -} diff --git a/pkgs/initiator/mocks/mock_initiator.go b/pkgs/initiator/mocks/mock_initiator.go deleted file mode 100644 index b56404c5..00000000 --- a/pkgs/initiator/mocks/mock_initiator.go +++ /dev/null @@ -1,180 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: client.go - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - client "github.com/bloxapp/ssv-dkg/pkgs/initiator" - dkg "github.com/bloxapp/ssv-dkg/pkgs/dkg" - wire "github.com/bloxapp/ssv-dkg/pkgs/wire" - types "github.com/bloxapp/ssv-spec/types" - common "github.com/ethereum/go-ethereum/common" - bls "github.com/herumi/bls-eth-go-binary/bls" - gomock "go.uber.org/mock/gomock" -) - -// MockDKGClient is a mock of DKGClient interface. -type MockDKGClient struct { - ctrl *gomock.Controller - recorder *MockDKGClientMockRecorder -} - -// MockDKGClientMockRecorder is the mock recorder for MockDKGClient. -type MockDKGClientMockRecorder struct { - mock *MockDKGClient -} - -// NewMockDKGClient creates a new mock instance. -func NewMockDKGClient(ctrl *gomock.Controller) *MockDKGClient { - mock := &MockDKGClient{ctrl: ctrl} - mock.recorder = &MockDKGClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDKGClient) EXPECT() *MockDKGClientMockRecorder { - return m.recorder -} - -// CreateVerifyFunc mocks base method. -func (m *MockDKGClient) CreateVerifyFunc(ops []*wire.Operator) (func(uint64, []byte, []byte) error, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateVerifyFunc", ops) - ret0, _ := ret[0].(func(uint64, []byte, []byte) error) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateVerifyFunc indicates an expected call of CreateVerifyFunc. -func (mr *MockDKGClientMockRecorder) CreateVerifyFunc(ops interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVerifyFunc", reflect.TypeOf((*MockDKGClient)(nil).CreateVerifyFunc), ops) -} - -// PakeMultiple mocks base method. -func (m *MockDKGClient) PakeMultiple(id [24]byte, allmsgs [][]byte) (*wire.MultipleSignedTransports, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PakeMultiple", id, allmsgs) - ret0, _ := ret[0].(*wire.MultipleSignedTransports) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PakeMultiple indicates an expected call of PakeMultiple. -func (mr *MockDKGClientMockRecorder) PakeMultiple(id, allmsgs interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PakeMultiple", reflect.TypeOf((*MockDKGClient)(nil).PakeMultiple), id, allmsgs) -} - -// ProcessDKGResultResponse mocks base method. -func (m *MockDKGClient) ProcessDKGResultResponse(responseResult [][]byte, id [24]byte) ([]dkg.Result, *bls.PublicKey, map[types.OperatorID]*bls.PublicKey, map[types.OperatorID]*bls.Sign, map[types.OperatorID]*bls.Sign, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProcessDKGResultResponse", responseResult, id) - ret0, _ := ret[0].([]dkg.Result) - ret1, _ := ret[1].(*bls.PublicKey) - ret2, _ := ret[2].(map[types.OperatorID]*bls.PublicKey) - ret3, _ := ret[3].(map[types.OperatorID]*bls.Sign) - ret4, _ := ret[4].(map[types.OperatorID]*bls.Sign) - ret5, _ := ret[5].(error) - return ret0, ret1, ret2, ret3, ret4, ret5 -} - -// ProcessDKGResultResponse indicates an expected call of ProcessDKGResultResponse. -func (mr *MockDKGClientMockRecorder) ProcessDKGResultResponse(responseResult, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProcessDKGResultResponse", reflect.TypeOf((*MockDKGClient)(nil).ProcessDKGResultResponse), responseResult, id) -} - -// SendAndCollect mocks base method. -func (m *MockDKGClient) SendAndCollect(op client.Operator, method string, data []byte) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendAndCollect", op, method, data) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendAndCollect indicates an expected call of SendAndCollect. -func (mr *MockDKGClientMockRecorder) SendAndCollect(op, method, data interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendAndCollect", reflect.TypeOf((*MockDKGClient)(nil).SendAndCollect), op, method, data) -} - -// SendExchangeMsgs mocks base method. -func (m *MockDKGClient) SendExchangeMsgs(exchangeMsgs [][]byte, id [24]byte) ([][]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendExchangeMsgs", exchangeMsgs, id) - ret0, _ := ret[0].([][]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendExchangeMsgs indicates an expected call of SendExchangeMsgs. -func (mr *MockDKGClientMockRecorder) SendExchangeMsgs(exchangeMsgs, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendExchangeMsgs", reflect.TypeOf((*MockDKGClient)(nil).SendExchangeMsgs), exchangeMsgs, id) -} - -// SendInitMsg mocks base method. -func (m *MockDKGClient) SendInitMsg(init *wire.Init, id [24]byte) ([][]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendInitMsg", init, id) - ret0, _ := ret[0].([][]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendInitMsg indicates an expected call of SendInitMsg. -func (mr *MockDKGClientMockRecorder) SendInitMsg(init, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendInitMsg", reflect.TypeOf((*MockDKGClient)(nil).SendInitMsg), init, id) -} - -// SendKyberMsgs mocks base method. -func (m *MockDKGClient) SendKyberMsgs(kyberDeals [][]byte, id [24]byte) ([][]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendKyberMsgs", kyberDeals, id) - ret0, _ := ret[0].([][]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendKyberMsgs indicates an expected call of SendKyberMsgs. -func (mr *MockDKGClientMockRecorder) SendKyberMsgs(kyberDeals, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendKyberMsgs", reflect.TypeOf((*MockDKGClient)(nil).SendKyberMsgs), kyberDeals, id) -} - -// SendToAll mocks base method. -func (m *MockDKGClient) SendToAll(method string, msg []byte) ([][]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendToAll", method, msg) - ret0, _ := ret[0].([][]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SendToAll indicates an expected call of SendToAll. -func (mr *MockDKGClientMockRecorder) SendToAll(method, msg interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendToAll", reflect.TypeOf((*MockDKGClient)(nil).SendToAll), method, msg) -} - -// StartDKG mocks base method. -func (m *MockDKGClient) StartDKG(withdraw []byte, ids []uint64, threshold uint64, fork [4]byte, forkName string, owner common.Address, nonce uint64) (*client.DepositDataJson, *client.KeyShares, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StartDKG", withdraw, ids, threshold, fork, forkName, owner, nonce) - ret0, _ := ret[0].(*client.DepositDataJson) - ret1, _ := ret[1].(*client.KeyShares) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// StartDKG indicates an expected call of StartDKG. -func (mr *MockDKGClientMockRecorder) StartDKG(withdraw, ids, threshold, fork, forkName, owner, nonce interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartDKG", reflect.TypeOf((*MockDKGClient)(nil).StartDKG), withdraw, ids, threshold, fork, forkName, owner, nonce) -} diff --git a/pkgs/operator/operator_test.go b/pkgs/operator/operator_test.go index 4d25645a..0fde4039 100644 --- a/pkgs/operator/operator_test.go +++ b/pkgs/operator/operator_test.go @@ -20,7 +20,7 @@ import ( const exmaplePath = "../../examples/" func TestRateLimit(t *testing.T) { - pk, err := load.EncryptedPrivateKey(exmaplePath+"server"+fmt.Sprintf("%v", 1)+"/encrypted_private_key.json", "12345678") + pk, err := load.EncryptedPrivateKey(exmaplePath+"operator"+fmt.Sprintf("%v", 1)+"/encrypted_private_key.json", "12345678") require.NoError(t, err) srv := New(pk) eg := errgroup.Group{} @@ -29,7 +29,7 @@ func TestRateLimit(t *testing.T) { }) t.Run("test init route rate limit", func(t *testing.T) { ops := make(map[uint64]initiator.Operator) - ops[1] = initiator.Operator{"http://localhost:3030", 1, &srv.State.privateKey.PublicKey} + ops[1] = initiator.Operator{"http://localhost:3030", 1, &srv.State.PrivateKey.PublicKey} parts := make([]*wire.Operator, 0, 0) for _, id := range []uint64{1} { diff --git a/pkgs/operator/state.go b/pkgs/operator/state.go index 9fad2f9f..68370e96 100644 --- a/pkgs/operator/state.go +++ b/pkgs/operator/state.go @@ -54,7 +54,7 @@ func (s *Switch) CreateInstance(reqID [24]byte, init *wire.Init, initiatorPublic } operatorID := uint64(0) - operatorPubKey := s.privateKey.Public().(*rsa.PublicKey) + operatorPubKey := s.PrivateKey.Public().(*rsa.PublicKey) pkBytes, err := crypto.EncodePublicKey(operatorPubKey) if err != nil { return nil, nil, err @@ -78,13 +78,13 @@ func (s *Switch) CreateInstance(reqID [24]byte, init *wire.Init, initiatorPublic } opts := dkg.OwnerOpts{ - Logger: s.logger.WithField("instance", hex.EncodeToString(reqID[:])), + Logger: s.Logger.WithField("instance", hex.EncodeToString(reqID[:])), BroadcastF: broadcast, SignFunc: s.Sign, VerifyFunc: verify, Suite: bls3.NewBLS12381Suite(), ID: operatorID, - OpPrivKey: s.privateKey, + OpPrivKey: s.PrivateKey, Owner: init.Owner, Nonce: init.Nonce, InitiatorPublicKey: initiatorPublicKey, @@ -98,13 +98,13 @@ func (s *Switch) CreateInstance(reqID [24]byte, init *wire.Init, initiatorPublic if err := owner.Broadcast(resp); err != nil { return nil, nil, err } - s.logger.Infof("Waiting for owner response to init") + s.Logger.Infof("Waiting for owner response to init") res := <-bchan return &instWrapper{owner, bchan, owner.ErrorChan}, res, nil } func (s *Switch) Sign(msg []byte) ([]byte, error) { - return crypto.SignRSA(s.privateKey, msg) + return crypto.SignRSA(s.PrivateKey, msg) } func (s *Switch) CreateVerifyFunc(ops []*wire.Operator) (func(id uint64, msg []byte, sig []byte) error, error) { @@ -127,34 +127,34 @@ func (s *Switch) CreateVerifyFunc(ops []*wire.Operator) (func(id uint64, msg []b } type Switch struct { - logger *logrus.Entry - mtx sync.RWMutex - instanceInitTime map[InstanceID]time.Time - instances map[InstanceID]Instance + Logger *logrus.Entry + Mtx sync.RWMutex + InstanceInitTime map[InstanceID]time.Time + Instances map[InstanceID]Instance - privateKey *rsa.PrivateKey + PrivateKey *rsa.PrivateKey //broadcastF func([]byte) error } func NewSwitch(pv *rsa.PrivateKey) *Switch { return &Switch{ - logger: logrus.NewEntry(logrus.New()), - mtx: sync.RWMutex{}, - instanceInitTime: make(map[InstanceID]time.Time, MaxInstances), - instances: make(map[InstanceID]Instance, MaxInstances), - privateKey: pv, + Logger: logrus.NewEntry(logrus.New()), + Mtx: sync.RWMutex{}, + InstanceInitTime: make(map[InstanceID]time.Time, MaxInstances), + Instances: make(map[InstanceID]Instance, MaxInstances), + PrivateKey: pv, } } func (s *Switch) InitInstance(reqID [24]byte, initMsg *wire.Transport, initiatorSignature []byte) ([]byte, error) { - logger := s.logger.WithField("reqid", hex.EncodeToString(reqID[:])) + logger := s.Logger.WithField("reqid", hex.EncodeToString(reqID[:])) logger.Infof("initializing DKG instance") init := &wire.Init{} if err := init.UnmarshalSSZ(initMsg.Data); err != nil { return nil, err } - s.logger.Debug("decoded init message") + s.Logger.Debug("decoded init message") // Check that incoming init message signature is valid initiatorPubKey, err := crypto.ParseRSAPubkey(init.InitiatorPublicKey) if err != nil { @@ -168,52 +168,52 @@ func (s *Switch) InitInstance(reqID [24]byte, initMsg *wire.Transport, initiator if err != nil { return nil, fmt.Errorf("init message signature isn't valid: %s", err.Error()) } - s.logger.Infof("init message signature is successfully verified, from: %x", sha256.Sum256(initiatorPubKey.N.Bytes())) - s.mtx.Lock() - l := len(s.instances) + s.Logger.Infof("init message signature is successfully verified, from: %x", sha256.Sum256(initiatorPubKey.N.Bytes())) + s.Mtx.Lock() + l := len(s.Instances) if l >= MaxInstances { - cleaned := s.cleanInstances() // not thread safe + cleaned := s.CleanInstances() // not thread safe if l-cleaned >= MaxInstances { - s.mtx.Unlock() + s.Mtx.Unlock() return nil, ErrMaxInstances } } - _, ok := s.instances[reqID] + _, ok := s.Instances[reqID] if ok { - tm := s.instanceInitTime[reqID] + tm := s.InstanceInitTime[reqID] if !time.Now().After(tm.Add(MaxInstanceTime)) { - s.mtx.Unlock() + s.Mtx.Unlock() return nil, ErrAlreadyExists } - delete(s.instances, reqID) - delete(s.instanceInitTime, reqID) + delete(s.Instances, reqID) + delete(s.InstanceInitTime, reqID) } - s.mtx.Unlock() + s.Mtx.Unlock() inst, resp, err := s.CreateInstance(reqID, init, initiatorPubKey) if err != nil { return nil, err } - s.mtx.Lock() - _, ok = s.instances[reqID] + s.Mtx.Lock() + _, ok = s.Instances[reqID] if ok { - s.mtx.Unlock() + s.Mtx.Unlock() return nil, ErrAlreadyExists } - s.instances[reqID] = inst - s.instanceInitTime[reqID] = time.Now() - s.mtx.Unlock() + s.Instances[reqID] = inst + s.InstanceInitTime[reqID] = time.Now() + s.Mtx.Unlock() return resp, nil } -func (s *Switch) cleanInstances() int { +func (s *Switch) CleanInstances() int { count := 0 - for id, instime := range s.instanceInitTime { + for id, instime := range s.InstanceInitTime { if time.Now().After(instime.Add(MaxInstanceTime)) { - delete(s.instances, id) - delete(s.instanceInitTime, id) + delete(s.Instances, id) + delete(s.InstanceInitTime, id) count++ } } @@ -230,9 +230,9 @@ func (s *Switch) ProcessMessage(dkgMsg []byte) ([]byte, error) { id := InstanceID(st.Identifier) - s.mtx.RLock() - inst, ok := s.instances[id] - s.mtx.RUnlock() + s.Mtx.RLock() + inst, ok := s.Instances[id] + s.Mtx.RUnlock() if !ok { return nil, ErrMissingInstance diff --git a/pkgs/operator/state_mock.go b/pkgs/operator/state_mock.go index 1480205d..a72fd47f 100644 --- a/pkgs/operator/state_mock.go +++ b/pkgs/operator/state_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" wire "github.com/bloxapp/ssv-dkg/pkgs/wire" - gomock "go.uber.org/mock/gomock" + gomock "github.com/golang/mock/gomock" ) // MockInstance is a mock of Instance interface. @@ -75,3 +75,17 @@ func (mr *MockInstanceMockRecorder) ReadResponse() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadResponse", reflect.TypeOf((*MockInstance)(nil).ReadResponse)) } + +// VerifyInitiatorMessage mocks base method. +func (m *MockInstance) VerifyInitiatorMessage(msg, sig []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VerifyInitiatorMessage", msg, sig) + ret0, _ := ret[0].(error) + return ret0 +} + +// VerifyInitiatorMessage indicates an expected call of VerifyInitiatorMessage. +func (mr *MockInstanceMockRecorder) VerifyInitiatorMessage(msg, sig interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyInitiatorMessage", reflect.TypeOf((*MockInstance)(nil).VerifyInitiatorMessage), msg, sig) +} diff --git a/pkgs/operator/state_test.go b/pkgs/operator/state_test.go index 49824275..4610a430 100644 --- a/pkgs/operator/state_test.go +++ b/pkgs/operator/state_test.go @@ -134,7 +134,7 @@ func TestInitInstance(t *testing.T) { require.NoError(t, err) require.NotNil(t, resp) - require.Len(t, swtch.instances, 1) + require.Len(t, swtch.Instances, 1) resp2, err2 := swtch.InitInstance(reqID, initMessage, sig) require.Equal(t, err2, ErrAlreadyExists) @@ -158,7 +158,7 @@ func TestInitInstance(t *testing.T) { require.True(t, tested) - swtch.instanceInitTime[reqID] = time.Now().Add(-6 * time.Minute) + swtch.InstanceInitTime[reqID] = time.Now().Add(-6 * time.Minute) resp, err = swtch.InitInstance(reqID, initMessage, sig) require.NoError(t, err) @@ -203,13 +203,13 @@ func TestSwitch_cleanInstances(t *testing.T) { resp, err := swtch.InitInstance(reqID, initMessage, sig) require.NoError(t, err) require.NotNil(t, resp) - require.Equal(t, swtch.cleanInstances(), 0) + require.Equal(t, swtch.CleanInstances(), 0) - require.Len(t, swtch.instances, 1) - swtch.instanceInitTime[reqID] = time.Now().Add(-time.Minute * 6) + require.Len(t, swtch.Instances, 1) + swtch.InstanceInitTime[reqID] = time.Now().Add(-time.Minute * 6) - require.Equal(t, swtch.cleanInstances(), 1) - require.Len(t, swtch.instances, 0) + require.Equal(t, swtch.CleanInstances(), 1) + require.Len(t, swtch.Instances, 0) }