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 }