github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/privval/signer_client_test.go (about)

     1  package privval
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/gnolang/gno/tm2/pkg/bft/types"
    12  	"github.com/gnolang/gno/tm2/pkg/random"
    13  	"github.com/gnolang/gno/tm2/pkg/testutils"
    14  )
    15  
    16  type signerTestCase struct {
    17  	chainID      string
    18  	mockPV       types.PrivValidator
    19  	signerClient *SignerClient
    20  	signerServer *SignerServer
    21  }
    22  
    23  func getSignerTestCases(t *testing.T) []signerTestCase {
    24  	t.Helper()
    25  
    26  	testCases := make([]signerTestCase, 0)
    27  
    28  	// Get test cases for each possible dialer (DialTCP / DialUnix / etc)
    29  	for _, dtc := range getDialerTestCases(t) {
    30  		chainID := random.RandStr(12)
    31  		mockPV := types.NewMockPV()
    32  
    33  		// get a pair of signer listener, signer dialer endpoints
    34  		sl, sd := getMockEndpoints(t, dtc.listener, dtc.dialer)
    35  		sc, err := NewSignerClient(sl)
    36  		require.NoError(t, err)
    37  		ss := NewSignerServer(sd, chainID, mockPV)
    38  
    39  		err = ss.Start()
    40  		require.NoError(t, err)
    41  
    42  		tc := signerTestCase{
    43  			chainID:      chainID,
    44  			mockPV:       mockPV,
    45  			signerClient: sc,
    46  			signerServer: ss,
    47  		}
    48  
    49  		testCases = append(testCases, tc)
    50  	}
    51  
    52  	return testCases
    53  }
    54  
    55  func TestSignerClose(t *testing.T) {
    56  	t.Parallel()
    57  
    58  	for _, tc := range getSignerTestCases(t) {
    59  		err := tc.signerClient.Close()
    60  		assert.NoError(t, err)
    61  
    62  		err = tc.signerServer.Stop()
    63  		assert.NoError(t, err)
    64  	}
    65  }
    66  
    67  func TestSignerPing(t *testing.T) {
    68  	t.Parallel()
    69  
    70  	for _, tc := range getSignerTestCases(t) {
    71  		defer tc.signerServer.Stop()
    72  		defer tc.signerClient.Close()
    73  
    74  		err := tc.signerClient.Ping()
    75  		assert.NoError(t, err)
    76  	}
    77  }
    78  
    79  func TestSignerGetPubKey(t *testing.T) {
    80  	t.Parallel()
    81  
    82  	for _, tc := range getSignerTestCases(t) {
    83  		defer tc.signerServer.Stop()
    84  		defer tc.signerClient.Close()
    85  
    86  		pubKey := tc.signerClient.GetPubKey()
    87  		expectedPubKey := tc.mockPV.GetPubKey()
    88  
    89  		assert.Equal(t, expectedPubKey, pubKey)
    90  
    91  		addr := tc.signerClient.GetPubKey().Address()
    92  		expectedAddr := tc.mockPV.GetPubKey().Address()
    93  
    94  		assert.Equal(t, expectedAddr, addr)
    95  	}
    96  }
    97  
    98  func TestSignerProposal(t *testing.T) {
    99  	t.Parallel()
   100  
   101  	for _, tc := range getSignerTestCases(t) {
   102  		ts := time.Now()
   103  		want := &types.Proposal{Timestamp: ts}
   104  		have := &types.Proposal{Timestamp: ts}
   105  
   106  		defer tc.signerServer.Stop()
   107  		defer tc.signerClient.Close()
   108  
   109  		require.NoError(t, tc.mockPV.SignProposal(tc.chainID, want))
   110  		require.NoError(t, tc.signerClient.SignProposal(tc.chainID, have))
   111  
   112  		assert.Equal(t, want.Signature, have.Signature)
   113  	}
   114  }
   115  
   116  func TestSignerVote(t *testing.T) {
   117  	t.Parallel()
   118  
   119  	for _, tc := range getSignerTestCases(t) {
   120  		ts := time.Now()
   121  		want := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   122  		have := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   123  
   124  		defer tc.signerServer.Stop()
   125  		defer tc.signerClient.Close()
   126  
   127  		require.NoError(t, tc.mockPV.SignVote(tc.chainID, want))
   128  		require.NoError(t, tc.signerClient.SignVote(tc.chainID, have))
   129  
   130  		assert.Equal(t, want.Signature, have.Signature)
   131  	}
   132  }
   133  
   134  func TestSignerVoteResetDeadline(t *testing.T) {
   135  	t.Parallel()
   136  
   137  	for _, tc := range getSignerTestCases(t) {
   138  		ts := time.Now()
   139  		want := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   140  		have := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   141  
   142  		defer tc.signerServer.Stop()
   143  		defer tc.signerClient.Close()
   144  
   145  		time.Sleep(testTimeoutReadWrite2o3)
   146  
   147  		require.NoError(t, tc.mockPV.SignVote(tc.chainID, want))
   148  		require.NoError(t, tc.signerClient.SignVote(tc.chainID, have))
   149  		assert.Equal(t, want.Signature, have.Signature)
   150  
   151  		// TODO(jleni): Clarify what is actually being tested
   152  
   153  		// This would exceed the deadline if it was not extended by the previous message
   154  		time.Sleep(testTimeoutReadWrite2o3)
   155  
   156  		require.NoError(t, tc.mockPV.SignVote(tc.chainID, want))
   157  		require.NoError(t, tc.signerClient.SignVote(tc.chainID, have))
   158  		assert.Equal(t, want.Signature, have.Signature)
   159  	}
   160  }
   161  
   162  func TestFlappySignerVoteKeepAlive(t *testing.T) {
   163  	t.Parallel()
   164  
   165  	testutils.FilterStability(t, testutils.Flappy)
   166  
   167  	for _, tc := range getSignerTestCases(t) {
   168  		ts := time.Now()
   169  		want := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   170  		have := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   171  
   172  		defer tc.signerServer.Stop()
   173  		defer tc.signerClient.Close()
   174  
   175  		// Check that even if the client does not request a
   176  		// signature for a long time. The service is still available
   177  
   178  		// in this particular case, we use the dialer logger to ensure that
   179  		// test messages are properly interleaved in the test logs
   180  		tc.signerServer.Logger.Debug("TEST: Forced Wait -------------------------------------------------")
   181  		time.Sleep(testTimeoutReadWrite * 3)
   182  		tc.signerServer.Logger.Debug("TEST: Forced Wait DONE---------------------------------------------")
   183  
   184  		require.NoError(t, tc.mockPV.SignVote(tc.chainID, want))
   185  		require.NoError(t, tc.signerClient.SignVote(tc.chainID, have))
   186  
   187  		assert.Equal(t, want.Signature, have.Signature)
   188  	}
   189  }
   190  
   191  func TestSignerSignProposalErrors(t *testing.T) {
   192  	t.Parallel()
   193  
   194  	for _, tc := range getSignerTestCases(t) {
   195  		// Replace service with a mock that always fails
   196  		tc.signerServer.privVal = types.NewErroringMockPV()
   197  		tc.mockPV = types.NewErroringMockPV()
   198  
   199  		defer tc.signerServer.Stop()
   200  		defer tc.signerClient.Close()
   201  
   202  		ts := time.Now()
   203  		proposal := &types.Proposal{Timestamp: ts}
   204  		err := tc.signerClient.SignProposal(tc.chainID, proposal)
   205  		require.Equal(t, err.(*RemoteSignerError).Description, types.ErrMockPV.Error())
   206  
   207  		err = tc.mockPV.SignProposal(tc.chainID, proposal)
   208  		require.Error(t, err)
   209  
   210  		err = tc.signerClient.SignProposal(tc.chainID, proposal)
   211  		require.Error(t, err)
   212  	}
   213  }
   214  
   215  func TestSignerSignVoteErrors(t *testing.T) {
   216  	t.Parallel()
   217  
   218  	for _, tc := range getSignerTestCases(t) {
   219  		ts := time.Now()
   220  		vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   221  
   222  		// Replace signer service privval with one that always fails
   223  		tc.signerServer.privVal = types.NewErroringMockPV()
   224  		tc.mockPV = types.NewErroringMockPV()
   225  
   226  		defer tc.signerServer.Stop()
   227  		defer tc.signerClient.Close()
   228  
   229  		err := tc.signerClient.SignVote(tc.chainID, vote)
   230  		require.Equal(t, err.(*RemoteSignerError).Description, types.ErrMockPV.Error())
   231  
   232  		err = tc.mockPV.SignVote(tc.chainID, vote)
   233  		require.Error(t, err)
   234  
   235  		err = tc.signerClient.SignVote(tc.chainID, vote)
   236  		require.Error(t, err)
   237  	}
   238  }
   239  
   240  func brokenHandler(privVal types.PrivValidator, request SignerMessage, chainID string) (SignerMessage, error) {
   241  	var res SignerMessage
   242  	var err error
   243  
   244  	switch r := request.(type) {
   245  	// This is broken and will answer most requests with a pubkey response
   246  	case *PubKeyRequest:
   247  		res = &PubKeyResponse{nil, nil}
   248  	case *SignVoteRequest:
   249  		res = &PubKeyResponse{nil, nil}
   250  	case *SignProposalRequest:
   251  		res = &PubKeyResponse{nil, nil}
   252  
   253  	case *PingRequest:
   254  		err, res = nil, &PingResponse{}
   255  
   256  	default:
   257  		err = fmt.Errorf("unknown msg: %v", r)
   258  	}
   259  
   260  	return res, err
   261  }
   262  
   263  func TestSignerUnexpectedResponse(t *testing.T) {
   264  	t.Parallel()
   265  
   266  	for _, tc := range getSignerTestCases(t) {
   267  		tc.signerServer.privVal = types.NewMockPV()
   268  		tc.mockPV = types.NewMockPV()
   269  
   270  		tc.signerServer.SetRequestHandler(brokenHandler)
   271  
   272  		defer tc.signerServer.Stop()
   273  		defer tc.signerClient.Close()
   274  
   275  		ts := time.Now()
   276  		want := &types.Vote{Timestamp: ts, Type: types.PrecommitType}
   277  
   278  		e := tc.signerClient.SignVote(tc.chainID, want)
   279  		assert.EqualError(t, e, "received unexpected response")
   280  	}
   281  }