code.vegaprotocol.io/vega@v0.79.0/wallet/api/node/retrying_node_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package node_test 17 18 import ( 19 "context" 20 "fmt" 21 "testing" 22 "time" 23 24 vgrand "code.vegaprotocol.io/vega/libs/rand" 25 apipb "code.vegaprotocol.io/vega/protos/vega/api/v1" 26 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 27 "code.vegaprotocol.io/vega/wallet/api/node" 28 "code.vegaprotocol.io/vega/wallet/api/node/mocks" 29 "code.vegaprotocol.io/vega/wallet/api/node/types" 30 31 "github.com/golang/mock/gomock" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 func TestRetryingNode_Statistics(t *testing.T) { 37 t.Run("Getting statistics is not retried", testRetryingNodeStatisticsNotRetried) 38 t.Run("Getting statistics succeeds", testRetryingNodeStatisticsSucceeds) 39 } 40 41 func testRetryingNodeStatisticsNotRetried(t *testing.T) { 42 // given 43 ctx := context.Background() 44 log := newTestLogger(t) 45 ttl := 5 * time.Second 46 47 // setup 48 adapter := newGRPCAdapterMock(t) 49 adapter.EXPECT().Host().AnyTimes().Return("test-client") 50 adapter.EXPECT().Statistics(gomock.Any()).Times(1).Return(types.Statistics{}, assert.AnError) 51 52 // when 53 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 54 response, err := retryingNode.Statistics(ctx) 55 56 // then 57 require.ErrorIs(t, err, assert.AnError) 58 assert.Empty(t, response) 59 } 60 61 func testRetryingNodeStatisticsSucceeds(t *testing.T) { 62 // given 63 ctx := context.Background() 64 log := newTestLogger(t) 65 ttl := 5 * time.Second 66 67 // setup 68 adapter := newGRPCAdapterMock(t) 69 adapter.EXPECT().Host().AnyTimes().Return("test-client") 70 statistics := types.Statistics{ 71 BlockHash: vgrand.RandomStr(5), 72 BlockHeight: 123456, 73 ChainID: vgrand.RandomStr(5), 74 VegaTime: vgrand.RandomStr(5), 75 } 76 adapter.EXPECT().Statistics(gomock.Any()).Times(1).Return(statistics, nil) 77 78 // when 79 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 80 response, err := retryingNode.Statistics(ctx) 81 82 // then 83 require.NoError(t, err) 84 assert.Equal(t, statistics, response) 85 } 86 87 func TestRetryingNode_LastBlock(t *testing.T) { 88 t.Run("Retrying with one successful call succeeds", testRetryingNodeLastBlockRetryingWithOneSuccessfulCallSucceeds) 89 t.Run("Retrying without successful calls fails", testRetryingNodeLastBlockRetryingWithoutSuccessfulCallsFails) 90 } 91 92 func testRetryingNodeLastBlockRetryingWithOneSuccessfulCallSucceeds(t *testing.T) { 93 // given 94 ctx := context.Background() 95 log := newTestLogger(t) 96 ttl := 5 * time.Second 97 98 // setup 99 expectedResponse := types.LastBlock{ 100 BlockHeight: 123, 101 BlockHash: vgrand.RandomStr(5), 102 ProofOfWorkHashFunction: vgrand.RandomStr(5), 103 ProofOfWorkDifficulty: 432, 104 } 105 adapter := newGRPCAdapterMock(t) 106 adapter.EXPECT().Host().AnyTimes().Return("test-client") 107 unsuccessfulCalls := adapter.EXPECT().LastBlock(gomock.Any()).Times(2).Return(types.LastBlock{}, assert.AnError) 108 successfulCall := adapter.EXPECT().LastBlock(gomock.Any()).Times(1).Return(expectedResponse, nil) 109 gomock.InOrder(unsuccessfulCalls, successfulCall) 110 111 // when 112 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 113 response, err := retryingNode.LastBlock(ctx) 114 115 // then 116 require.NoError(t, err) 117 assert.Equal(t, expectedResponse, response) 118 } 119 120 func testRetryingNodeLastBlockRetryingWithoutSuccessfulCallsFails(t *testing.T) { 121 // given 122 ctx := context.Background() 123 log := newTestLogger(t) 124 ttl := 5 * time.Second 125 126 // setup 127 adapter := newGRPCAdapterMock(t) 128 adapter.EXPECT().Host().AnyTimes().Return("test-client") 129 adapter.EXPECT().LastBlock(gomock.Any()).Times(4).Return(types.LastBlock{}, assert.AnError) 130 131 // when 132 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 133 nodeID, err := retryingNode.LastBlock(ctx) 134 135 // then 136 require.Error(t, err, assert.AnError) 137 assert.Empty(t, nodeID) 138 } 139 140 func TestRetryingNode_CheckTransaction(t *testing.T) { 141 t.Run("Retrying with one successful call succeeds", testRetryingNodeCheckTransactionRetryingWithOneSuccessfulCallSucceeds) 142 t.Run("Retrying with a successful call but unsuccessful transaction fails", testRetryingNodeCheckTransactionWithSuccessfulCallBuUnsuccessfulTxFails) 143 t.Run("Retrying without successful calls fails", testRetryingNodeCheckTransactionRetryingWithoutSuccessfulCallsFails) 144 } 145 146 func testRetryingNodeCheckTransactionRetryingWithOneSuccessfulCallSucceeds(t *testing.T) { 147 // given 148 ctx := context.Background() 149 log := newTestLogger(t) 150 tx := &commandspb.Transaction{ 151 Version: 3, 152 InputData: []byte{}, 153 Signature: &commandspb.Signature{ 154 Value: "345678", 155 Algo: vgrand.RandomStr(5), 156 Version: 2, 157 }, 158 From: &commandspb.Transaction_PubKey{ 159 PubKey: vgrand.RandomStr(5), 160 }, 161 Pow: &commandspb.ProofOfWork{ 162 Tid: vgrand.RandomStr(5), 163 Nonce: 23214, 164 }, 165 } 166 expectedResponse := &apipb.CheckTransactionResponse{ 167 Success: true, 168 } 169 ttl := 5 * time.Second 170 171 // setup 172 request := &apipb.CheckTransactionRequest{ 173 Tx: tx, 174 } 175 adapter := newGRPCAdapterMock(t) 176 adapter.EXPECT().Host().AnyTimes().Return("test-client") 177 gomock.InOrder( 178 adapter.EXPECT().CheckTransaction(gomock.Any(), request).Times(2).Return(nil, assert.AnError), 179 adapter.EXPECT().CheckTransaction(gomock.Any(), request).Times(1).Return(expectedResponse, nil), 180 ) 181 182 // when 183 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 184 err := retryingNode.CheckTransaction(ctx, tx) 185 186 // then 187 require.NoError(t, err) 188 } 189 190 func testRetryingNodeCheckTransactionWithSuccessfulCallBuUnsuccessfulTxFails(t *testing.T) { 191 // given 192 ctx := context.Background() 193 log := newTestLogger(t) 194 tx := &commandspb.Transaction{ 195 Version: 3, 196 InputData: []byte{}, 197 Signature: &commandspb.Signature{ 198 Value: "345678", 199 Algo: vgrand.RandomStr(5), 200 Version: 2, 201 }, 202 From: &commandspb.Transaction_PubKey{ 203 PubKey: vgrand.RandomStr(5), 204 }, 205 Pow: &commandspb.ProofOfWork{ 206 Tid: vgrand.RandomStr(5), 207 Nonce: 23214, 208 }, 209 } 210 ttl := 5 * time.Second 211 212 // setup 213 request := &apipb.CheckTransactionRequest{ 214 Tx: tx, 215 } 216 expectedResponse := &apipb.CheckTransactionResponse{ 217 Success: false, 218 Code: 42, 219 Data: vgrand.RandomStr(10), 220 } 221 adapter := newGRPCAdapterMock(t) 222 adapter.EXPECT().Host().AnyTimes().Return("test-client") 223 unsuccessfulCalls := adapter.EXPECT().CheckTransaction(gomock.Any(), request).Times(2).Return(nil, assert.AnError) 224 successfulCall := adapter.EXPECT().CheckTransaction(gomock.Any(), request).Times(1).Return(expectedResponse, nil) 225 gomock.InOrder(unsuccessfulCalls, successfulCall) 226 227 // when 228 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 229 err := retryingNode.CheckTransaction(ctx, tx) 230 231 // then 232 require.EqualError(t, err, fmt.Sprintf("%s (ABCI code %d)", expectedResponse.Data, expectedResponse.Code)) 233 } 234 235 func testRetryingNodeCheckTransactionRetryingWithoutSuccessfulCallsFails(t *testing.T) { 236 // given 237 ctx := context.Background() 238 log := newTestLogger(t) 239 tx := &commandspb.Transaction{ 240 Version: 3, 241 InputData: []byte{}, 242 Signature: &commandspb.Signature{ 243 Value: "345678", 244 Algo: vgrand.RandomStr(5), 245 Version: 2, 246 }, 247 From: &commandspb.Transaction_PubKey{ 248 PubKey: vgrand.RandomStr(5), 249 }, 250 Pow: &commandspb.ProofOfWork{ 251 Tid: vgrand.RandomStr(5), 252 Nonce: 23214, 253 }, 254 } 255 ttl := 5 * time.Second 256 257 // setup 258 adapter := newGRPCAdapterMock(t) 259 adapter.EXPECT().Host().AnyTimes().Return("test-client") 260 adapter.EXPECT().CheckTransaction(gomock.Any(), &apipb.CheckTransactionRequest{ 261 Tx: tx, 262 }).Times(4).Return(nil, assert.AnError) 263 264 // when 265 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 266 err := retryingNode.CheckTransaction(ctx, tx) 267 268 // then 269 require.Error(t, err, assert.AnError) 270 } 271 272 func TestRetryingNode_SendTransaction(t *testing.T) { 273 t.Run("Retrying with one successful call succeeds", testRetryingNodeSendTransactionRetryingWithOneSuccessfulCallSucceeds) 274 t.Run("Retrying with a successful call but unsuccessful transaction fails", testRetryingNodeSendTransactionWithSuccessfulCallBuUnsuccessfulTxFails) 275 t.Run("Retrying without successful calls fails", testRetryingNodeSendTransactionRetryingWithoutSuccessfulCallsFails) 276 } 277 278 func testRetryingNodeSendTransactionRetryingWithOneSuccessfulCallSucceeds(t *testing.T) { 279 // given 280 ctx := context.Background() 281 log := newTestLogger(t) 282 expectedTxHash := vgrand.RandomStr(10) 283 tx := &commandspb.Transaction{ 284 Version: 3, 285 InputData: []byte{}, 286 Signature: &commandspb.Signature{ 287 Value: "345678", 288 Algo: vgrand.RandomStr(5), 289 Version: 2, 290 }, 291 From: &commandspb.Transaction_PubKey{ 292 PubKey: vgrand.RandomStr(5), 293 }, 294 Pow: &commandspb.ProofOfWork{ 295 Tid: vgrand.RandomStr(5), 296 Nonce: 23214, 297 }, 298 } 299 ttl := 5 * time.Second 300 301 // setup 302 request := &apipb.SubmitTransactionRequest{ 303 Tx: tx, 304 Type: apipb.SubmitTransactionRequest_TYPE_SYNC, 305 } 306 expectedResponse := &apipb.SubmitTransactionResponse{ 307 Success: true, 308 TxHash: expectedTxHash, 309 } 310 adapter := newGRPCAdapterMock(t) 311 adapter.EXPECT().Host().AnyTimes().Return("test-client") 312 unsuccessfulCalls := adapter.EXPECT().SubmitTransaction(gomock.Any(), request).Times(2).Return(nil, assert.AnError) 313 successfulCall := adapter.EXPECT().SubmitTransaction(gomock.Any(), request).Times(1).Return(expectedResponse, nil) 314 gomock.InOrder(unsuccessfulCalls, successfulCall) 315 316 // when 317 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 318 response, err := retryingNode.SendTransaction(ctx, tx, apipb.SubmitTransactionRequest_TYPE_SYNC) 319 320 // then 321 require.NoError(t, err) 322 assert.Equal(t, expectedResponse.TxHash, response) 323 } 324 325 func testRetryingNodeSendTransactionWithSuccessfulCallBuUnsuccessfulTxFails(t *testing.T) { 326 // given 327 ctx := context.Background() 328 log := newTestLogger(t) 329 expectedTxHash := vgrand.RandomStr(10) 330 tx := &commandspb.Transaction{ 331 Version: 3, 332 InputData: []byte{}, 333 Signature: &commandspb.Signature{ 334 Value: "345678", 335 Algo: vgrand.RandomStr(5), 336 Version: 2, 337 }, 338 From: &commandspb.Transaction_PubKey{ 339 PubKey: vgrand.RandomStr(5), 340 }, 341 Pow: &commandspb.ProofOfWork{ 342 Tid: vgrand.RandomStr(5), 343 Nonce: 23214, 344 }, 345 } 346 ttl := 5 * time.Second 347 348 // setup 349 request := &apipb.SubmitTransactionRequest{ 350 Tx: tx, 351 Type: apipb.SubmitTransactionRequest_TYPE_SYNC, 352 } 353 expectedResponse := &apipb.SubmitTransactionResponse{ 354 Success: false, 355 TxHash: expectedTxHash, 356 Code: 42, 357 Data: vgrand.RandomStr(10), 358 } 359 adapter := newGRPCAdapterMock(t) 360 adapter.EXPECT().Host().AnyTimes().Return("test-client") 361 unsuccessfulCalls := adapter.EXPECT().SubmitTransaction(gomock.Any(), request).Times(2).Return(nil, assert.AnError) 362 successfulCall := adapter.EXPECT().SubmitTransaction(gomock.Any(), request).Times(1).Return(expectedResponse, nil) 363 gomock.InOrder(unsuccessfulCalls, successfulCall) 364 365 // when 366 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 367 response, err := retryingNode.SendTransaction(ctx, tx, apipb.SubmitTransactionRequest_TYPE_SYNC) 368 369 // then 370 require.EqualError(t, err, fmt.Sprintf("%s (ABCI code %d)", expectedResponse.Data, expectedResponse.Code)) 371 assert.Empty(t, response) 372 } 373 374 func testRetryingNodeSendTransactionRetryingWithoutSuccessfulCallsFails(t *testing.T) { 375 // given 376 ctx := context.Background() 377 log := newTestLogger(t) 378 tx := &commandspb.Transaction{ 379 Version: 3, 380 InputData: []byte{}, 381 Signature: &commandspb.Signature{ 382 Value: "345678", 383 Algo: vgrand.RandomStr(5), 384 Version: 2, 385 }, 386 From: &commandspb.Transaction_PubKey{ 387 PubKey: vgrand.RandomStr(5), 388 }, 389 Pow: &commandspb.ProofOfWork{ 390 Tid: vgrand.RandomStr(5), 391 Nonce: 23214, 392 }, 393 } 394 ttl := 5 * time.Second 395 396 // setup 397 adapter := newGRPCAdapterMock(t) 398 adapter.EXPECT().Host().AnyTimes().Return("test-client") 399 adapter.EXPECT().SubmitTransaction(gomock.Any(), &apipb.SubmitTransactionRequest{ 400 Tx: tx, 401 Type: apipb.SubmitTransactionRequest_TYPE_SYNC, 402 }).Times(4).Return(nil, assert.AnError) 403 404 // when 405 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 406 resp, err := retryingNode.SendTransaction(ctx, tx, apipb.SubmitTransactionRequest_TYPE_SYNC) 407 408 // then 409 require.Error(t, err, assert.AnError) 410 assert.Empty(t, resp) 411 } 412 413 func TestRetryingNode_Stop(t *testing.T) { 414 t.Run("Stopping the node closes the underlying adapter", testRetryingNodeStoppingNodeClosesUnderlyingAdapter) 415 t.Run("Stopping the node returns the underlying adapter error if any", testRetryingNodeStoppingNodeReturnUnderlyingErrorIfAny) 416 } 417 418 func testRetryingNodeStoppingNodeClosesUnderlyingAdapter(t *testing.T) { 419 // given 420 log := newTestLogger(t) 421 ttl := 5 * time.Second 422 423 // setup 424 adapter := newGRPCAdapterMock(t) 425 adapter.EXPECT().Host().AnyTimes().Return("test-client") 426 adapter.EXPECT().Stop().Times(1).Return(nil) 427 428 // when 429 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 430 err := retryingNode.Stop() 431 432 // then 433 require.NoError(t, err) 434 } 435 436 func testRetryingNodeStoppingNodeReturnUnderlyingErrorIfAny(t *testing.T) { 437 // given 438 log := newTestLogger(t) 439 ttl := 5 * time.Second 440 441 // setup 442 adapter := newGRPCAdapterMock(t) 443 adapter.EXPECT().Host().AnyTimes().Return("test-client") 444 adapter.EXPECT().Stop().Times(1).Return(assert.AnError) 445 446 // when 447 retryingNode := node.BuildRetryingNode(log, adapter, 3, ttl) 448 err := retryingNode.Stop() 449 450 // then 451 require.EqualError(t, err, fmt.Errorf("could not close properly stop the gRPC API client: %w", assert.AnError).Error()) 452 } 453 454 func newGRPCAdapterMock(t *testing.T) *mocks.MockGRPCAdapter { 455 t.Helper() 456 ctrl := gomock.NewController(t) 457 grpcAdapter := mocks.NewMockGRPCAdapter(ctrl) 458 return grpcAdapter 459 }