Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Ping multisig #137

Merged
merged 6 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions integration_test/health_check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package integration_test

import (
"io"
"os"
"strings"
"testing"

"github.com/bloxapp/ssv/logging"
"github.com/ethereum/go-ethereum"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"

"github.com/ssvlabs/dkg-spec/testing/stubs"
cli_initiator "github.com/ssvlabs/ssv-dkg/cli/initiator"
)

func TestHealthCheck(t *testing.T) {
err := logging.SetGlobalLogger("info", "capital", "console", nil)
require.NoError(t, err)
version := "test.version"
stubClient := &stubs.Client{
CallContractF: func(call ethereum.CallMsg) ([]byte, error) {
return nil, nil
},
}
servers, _ := createOperators(t, version, stubClient)
var ips []string
for _, s := range servers {
ips = append(ips, s.HttpSrv.URL)
}
RootCmd := &cobra.Command{
Use: "ssv-dkg",
Short: "CLI for running Distributed Key Generation protocol",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
},
}
RootCmd.AddCommand(cli_initiator.HealthCheck)
RootCmd.Short = "ssv-dkg-test"
RootCmd.Version = version
cli_initiator.HealthCheck.Version = version
t.Run("test 1 operator health check: positive", func(t *testing.T) {
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
args := []string{"ping", "--ip", ips[0]}
RootCmd.SetArgs(args)
err := RootCmd.Execute()
require.NoError(t, err)
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = rescueStdout
t.Log(string(out))
require.True(t, strings.Contains(string(out), "operator online and healthy: multisig ready 👌 and connected to ethereum network"))
resetFlags(RootCmd)
})
t.Run("test 1 operator health check: negative", func(t *testing.T) {
servers[0].HttpSrv.Close()
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
args := []string{"ping", "--ip", ips[0]}
RootCmd.SetArgs(args)
err := RootCmd.Execute()
require.NoError(t, err)
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = rescueStdout
t.Log(string(out))
require.True(t, strings.Contains(string(out), "operator not healthy"))
require.True(t, strings.Contains(string(out), "connection refused"))
resetFlags(RootCmd)
})
for _, srv := range servers {
srv.HttpSrv.Close()
}
}
30 changes: 20 additions & 10 deletions pkgs/initiator/initiator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/herumi/bls-eth-go-binary/bls"
"github.com/imroc/req/v3"
"github.com/ssvlabs/ssv-dkg/pkgs/consts"
"github.com/ssvlabs/ssv-dkg/pkgs/crypto"
"github.com/ssvlabs/ssv-dkg/pkgs/utils"
"github.com/ssvlabs/ssv-dkg/pkgs/wire"
"go.uber.org/zap"

spec "github.com/ssvlabs/dkg-spec"
spec_crypto "github.com/ssvlabs/dkg-spec/crypto"
"github.com/ssvlabs/ssv-dkg/pkgs/consts"
"github.com/ssvlabs/ssv-dkg/pkgs/crypto"
"github.com/ssvlabs/ssv-dkg/pkgs/utils"
"github.com/ssvlabs/ssv-dkg/pkgs/wire"
)

type VerifyMessageSignatureFunc func(pub *rsa.PublicKey, msg, sig []byte) error
Expand Down Expand Up @@ -718,7 +718,7 @@ func (c *Initiator) Ping(ips []string) error {
res := <-resc
err := c.processPongMessage(res)
if err != nil {
c.Logger.Error("😥 Operator not healthy: ", zap.Error(err), zap.String("IP", res.IP))
c.Logger.Error("🔴 operator not healthy: ", zap.Error(err), zap.String("IP", res.IP))
continue
}
}
Expand Down Expand Up @@ -782,20 +782,30 @@ func (c *Initiator) processPongMessage(res wire.PongResult) error {
}
pong := &wire.Pong{}
if err := pong.UnmarshalSSZ(signedPongMsg.Message.Data); err != nil {
return err
return fmt.Errorf("🆘 cant unmarshall pong message, probably old version, please upgrade: %w", err)

}
pongBytes, err := signedPongMsg.Message.MarshalSSZ()
if err != nil {
return err
return fmt.Errorf("error marshalling signedPongMsg: %w", err)
}
pub, err := spec_crypto.ParseRSAPublicKey(pong.PubKey)
if err != nil {
return err
return fmt.Errorf("cant parse RSA public key from pong message: %w", err)
}
// Check that we got pong with correct pub
if err := spec_crypto.VerifyRSA(pub, pongBytes, signedPongMsg.Signature); err != nil {
return err
return fmt.Errorf("operator sent pong with wrong RSA public key %w", err)
}
if pong.Multisig {
if pong.EthClientConnected {
c.Logger.Info("🟢 operator online and healthy: multisig ready 👌 and connected to ethereum network ⛓️", zap.Uint64("ID", pong.ID), zap.String("IP", res.IP), zap.String("Version", string(signedPongMsg.Message.Version)), zap.String("Public key", string(pong.PubKey)))
} else {
c.Logger.Info("🟢 operator online and healthy: multisig ready 👌 but NOT connected to ethereum network 🚫", zap.Uint64("ID", pong.ID), zap.String("IP", res.IP), zap.String("Version", string(signedPongMsg.Message.Version)), zap.String("Public key", string(pong.PubKey)))
}
} else {
c.Logger.Error("🔴 operator online: but NOT multisig ready", zap.Uint64("ID", pong.ID), zap.String("IP", res.IP), zap.String("Version", string(signedPongMsg.Message.Version)), zap.String("Public key", string(pong.PubKey)))
}
c.Logger.Info("🍎 operator online and healthy", zap.Uint64("ID", pong.ID), zap.String("IP", res.IP), zap.String("Version", string(signedPongMsg.Message.Version)), zap.String("Public key", string(pong.PubKey)))
return nil
}

Expand Down
12 changes: 10 additions & 2 deletions pkgs/operator/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package operator

import (
"bytes"
"context"
"crypto/rsa"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -163,9 +164,16 @@ func (s *Switch) MarshallAndSign(msg wire.SSZMarshaller, msgType wire.TransportT
}

func (s *Switch) Pong() ([]byte, error) {
var connected bool
latestBlock, err := s.EthClient.BlockNumber(context.Background())
if latestBlock > 0 && err == nil {
connected = true
}
pong := &wire.Pong{
ID: s.OperatorID,
PubKey: s.PubKeyBytes,
ID: s.OperatorID,
PubKey: s.PubKeyBytes,
Multisig: true,
EthClientConnected: connected,
}
return s.MarshallAndSign(pong, wire.PongMessageType, s.OperatorID, [24]byte{})
}
Expand Down
13 changes: 4 additions & 9 deletions pkgs/wire/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,11 @@ type Exchange struct {
Commits []byte `ssz-max:"2048"`
}

type Ping struct {
// Operators involved in the DKG
Operators []*spec.Operator `ssz-max:"13"`
// Initiator public key
InitiatorPublicKey []byte `ssz-max:"2048"`
}

type Pong struct {
ID uint64
PubKey []byte `ssz-max:"2048"`
ID uint64
PubKey []byte `ssz-max:"2048"`
Multisig bool
EthClientConnected bool
Comment on lines +116 to +117
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need both? isn't it enough just multisig? it won't be working without eth, right ?

Copy link
Contributor Author

@pavelkrolevets pavelkrolevets Oct 4, 2024

Choose a reason for hiding this comment

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

we check connection to eth client when start operator, if no it cant start, but we can loose it after we started. So we can be Multisig=true and EthClientConnected=false.
For old operator versions we dont have both fields so it will be false, false.
If we have only EthClientConnected then we cant distinguish between old and new version, only comparing version. So Multisig is rather informational for users not to compare versions, as old will be always false

Please let me know if it makes sense.

}

type ResultData struct {
Expand Down
Loading
Loading