github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/tests/test_responder/tests.py (about) 1 # Copyright 2017 Intel Corporation 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 # pylint: disable=invalid-name 16 17 import unittest 18 19 from sawtooth_validator.protobuf import network_pb2 20 from sawtooth_validator.protobuf import validator_pb2 21 from sawtooth_validator.protobuf import block_pb2 22 from sawtooth_validator.protobuf import batch_pb2 23 from sawtooth_validator.protobuf import transaction_pb2 24 from sawtooth_validator.journal.responder import Responder 25 from sawtooth_validator.journal.responder import BlockResponderHandler 26 from sawtooth_validator.journal.responder import BatchByBatchIdResponderHandler 27 from sawtooth_validator.journal.responder import \ 28 BatchByTransactionIdResponderHandler 29 from sawtooth_validator.journal.responder import ResponderBlockResponseHandler 30 from sawtooth_validator.journal.responder import ResponderBatchResponseHandler 31 from test_responder.mock import MockGossip 32 from test_responder.mock import MockCompleter 33 34 35 class TestResponder(unittest.TestCase): 36 def setUp(self): 37 self.gossip = MockGossip() 38 self.completer = MockCompleter() 39 self.responder = Responder(self.completer) 40 self.block_request_handler = \ 41 BlockResponderHandler(self.responder, self.gossip) 42 self.block_response_handler = \ 43 ResponderBlockResponseHandler(self.responder, self.gossip) 44 self.batch_request_handler = \ 45 BatchByBatchIdResponderHandler(self.responder, self.gossip) 46 self.batch_response_handler = \ 47 ResponderBatchResponseHandler(self.responder, self.gossip) 48 self.batch_by_txn_request_handler = \ 49 BatchByTransactionIdResponderHandler(self.responder, self.gossip) 50 51 # Tests 52 def test_block_responder_handler(self): 53 """ 54 Test that the BlockResponderHandler correctly broadcasts a received 55 request that the Responder cannot respond to, or sends a 56 GossipBlockResponse back to the connection_id the handler received 57 the request from. 58 """ 59 # The completer does not have the requested block 60 before_message = network_pb2.GossipBlockRequest( 61 block_id="ABC", 62 nonce="1", 63 time_to_live=1) 64 65 after_message = network_pb2.GossipBlockRequest( 66 block_id="ABC", 67 nonce="1", 68 time_to_live=0) 69 70 self.block_request_handler.handle( 71 "Connection_1", before_message.SerializeToString()) 72 # If we cannot respond to the request, broadcast the block request 73 # and add to pending request 74 self.assert_message_was_broadcasted( 75 after_message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) 76 77 self.assert_request_pending( 78 requested_id="ABC", connection_id="Connection_1") 79 self.assert_message_not_sent(connection_id="Connection_1") 80 81 # Add the block to the completer and resend the Block Request 82 block = block_pb2.Block(header_signature="ABC") 83 self.completer.add_block(block) 84 85 message = network_pb2.GossipBlockRequest( 86 block_id="ABC", 87 nonce="2", 88 time_to_live=1) 89 90 self.block_request_handler.handle( 91 "Connection_1", message.SerializeToString()) 92 93 # Check that the a Block Response was sent back to "Connection_1" 94 self.assert_message_sent( 95 connection_id="Connection_1", 96 message_type=validator_pb2.Message.GOSSIP_BLOCK_RESPONSE 97 ) 98 99 def test_block_responder_handler_requested(self): 100 """ 101 Test that the BlockResponderHandler correctly broadcasts a received 102 request that the Responder cannot respond to, and does not rebroadcast 103 the same request. If we have already recieved the 104 request, do nothing. 105 """ 106 before_message = network_pb2.GossipBlockRequest( 107 block_id="ABC", 108 nonce="1", 109 time_to_live=1) 110 111 after_message = network_pb2.GossipBlockRequest( 112 block_id="ABC", 113 nonce="1", 114 time_to_live=0) 115 116 self.block_request_handler.handle( 117 "Connection_1", before_message.SerializeToString()) 118 # If we cannot respond to the request, broadcast the block request 119 # and add to pending request 120 self.assert_message_was_broadcasted( 121 after_message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) 122 123 self.assert_request_pending( 124 requested_id="ABC", connection_id="Connection_1") 125 self.assert_message_not_sent(connection_id="Connection_1") 126 127 self.gossip.clear() 128 129 # Message should be dropped since the same message has already been 130 # handled 131 self.block_request_handler.handle( 132 "Connection_2", before_message.SerializeToString()) 133 134 self.assert_message_was_not_broadcasted( 135 before_message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) 136 137 self.assert_request_not_pending( 138 requested_id="ABC", connection_id="Connection_2") 139 140 message = network_pb2.GossipBlockRequest( 141 block_id="ABC", 142 nonce="2", 143 time_to_live=1) 144 145 self.block_request_handler.handle( 146 "Connection_2", message.SerializeToString()) 147 148 self.assert_message_was_not_broadcasted( 149 message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) 150 151 self.assert_request_pending( 152 requested_id="ABC", connection_id="Connection_2") 153 self.assert_message_not_sent(connection_id="Connection_2") 154 155 def test_responder_block_response_handler(self): 156 """ 157 Test that the ResponderBlockResponseHandler, after receiving a Block 158 Response, checks to see if the responder has any pending request for 159 that response and forwards the response on to the connection_id that 160 had requested it. 161 """ 162 # The Responder does not have any pending requests for block "ABC" 163 block = block_pb2.Block(header_signature="ABC") 164 response_message = network_pb2.GossipBlockResponse( 165 content=block.SerializeToString()) 166 167 self.block_response_handler.handle( 168 "Connection_1", (block, response_message.SerializeToString())) 169 170 # ResponderBlockResponseHandler should not send any messages. 171 self.assert_message_not_sent("Connection_1") 172 self.assert_request_not_pending(requested_id="ABC") 173 174 # Handle a request message for block "ABC". This adds it to the pending 175 # request queue. 176 request_message = \ 177 network_pb2.GossipBlockRequest(block_id="ABC", time_to_live=1) 178 179 self.block_request_handler.handle( 180 "Connection_2", request_message.SerializeToString()) 181 182 self.assert_request_pending( 183 requested_id="ABC", connection_id="Connection_2") 184 185 # Handle the the BlockResponse Message. Since Connection_2 had 186 # requested the block but it could not be fulfilled at that time of the 187 # request the received BlockResponse is forwarded to Connection_2 188 self.block_response_handler.handle( 189 "Connection_1", (block, response_message.SerializeToString())) 190 191 self.assert_message_sent( 192 connection_id="Connection_2", 193 message_type=validator_pb2.Message.GOSSIP_BLOCK_RESPONSE 194 ) 195 # The request for block "ABC" from "Connection_2" is no longer pending 196 # it should be removed from the pending request cache. 197 self.assert_request_not_pending(requested_id="ABC") 198 199 def test_batch_by_id_responder_handler(self): 200 """ 201 Test that the BatchByBatchIdResponderHandler correctly broadcasts a 202 received request that the Responder cannot respond to, or sends a 203 GossipBatchResponse back to the connection_id the handler received 204 the request from. 205 """ 206 # The completer does not have the requested batch 207 before_message = network_pb2.GossipBatchByBatchIdRequest( 208 id="abc", 209 nonce="1", 210 time_to_live=1) 211 212 after_message = network_pb2.GossipBatchByBatchIdRequest( 213 id="abc", 214 nonce="1", 215 time_to_live=0) 216 217 self.batch_request_handler.handle( 218 "Connection_1", before_message.SerializeToString()) 219 # If we cannot respond to the request broadcast batch request and add 220 # to pending request 221 self.assert_message_was_broadcasted( 222 after_message, 223 validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST) 224 self.assert_request_pending( 225 requested_id="abc", connection_id="Connection_1") 226 self.assert_message_not_sent(connection_id="Connection_1") 227 228 # Add the batch to the completer and resend the BatchByBatchIdRequest 229 message = network_pb2.GossipBatchByBatchIdRequest( 230 id="abc", 231 nonce="2", 232 time_to_live=1) 233 batch = batch_pb2.Batch(header_signature="abc") 234 self.completer.add_batch(batch) 235 self.batch_request_handler.handle( 236 "Connection_1", message.SerializeToString()) 237 238 # Check that the a Batch Response was sent back to "Connection_1" 239 self.assert_message_sent( 240 connection_id="Connection_1", 241 message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE 242 ) 243 244 def test_batch_by_id_responder_handler_requested(self): 245 """ 246 Test that the BatchByBatchIdResponderHandler correctly broadcasts 247 a received request that the Responder cannot respond to, and does not 248 rebroadcast the same request again. If we have already recieved the 249 request, do nothing. 250 """ 251 # The completer does not have the requested batch 252 before_message = network_pb2.GossipBatchByBatchIdRequest( 253 id="abc", 254 nonce="1", 255 time_to_live=1) 256 257 after_message = network_pb2.GossipBatchByBatchIdRequest( 258 id="abc", 259 nonce="1", 260 time_to_live=0) 261 self.batch_request_handler.handle( 262 "Connection_1", before_message.SerializeToString()) 263 # If we cannot respond to the request broadcast batch request and add 264 # to pending request 265 self.assert_message_was_broadcasted( 266 after_message, 267 validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST) 268 self.assert_request_pending( 269 requested_id="abc", connection_id="Connection_1") 270 self.assert_message_not_sent(connection_id="Connection_1") 271 272 self.gossip.clear() 273 274 # Message should be dropped since the same message has already been 275 # handled 276 self.batch_request_handler.handle( 277 "Connection_2", before_message.SerializeToString()) 278 279 self.assert_message_was_not_broadcasted( 280 before_message, 281 validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST) 282 283 self.assert_request_not_pending( 284 requested_id="abc", connection_id="Connection_2") 285 286 message = network_pb2.GossipBatchByBatchIdRequest( 287 id="abc", 288 nonce="2", 289 time_to_live=1) 290 291 self.batch_request_handler.handle( 292 "Connection_2", message.SerializeToString()) 293 294 self.assert_message_was_not_broadcasted( 295 message, validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST) 296 297 self.assert_request_pending( 298 requested_id="abc", connection_id="Connection_2") 299 self.assert_message_not_sent(connection_id="Connection_2") 300 301 def test_batch_by_transaction_id_response_handler(self): 302 """ 303 Test that the BatchByTransactionIdResponderHandler correctly broadcasts 304 a received request that the Responder cannot respond to, or sends a 305 GossipBatchResponse back to the connection_id the handler received 306 the request from. 307 """ 308 # The completer does not have the requested batch with the transaction 309 before_message = network_pb2.GossipBatchByTransactionIdRequest( 310 ids=["123"], 311 nonce="1", 312 time_to_live=1) 313 314 after_message = network_pb2.GossipBatchByTransactionIdRequest( 315 ids=["123"], 316 nonce="1", 317 time_to_live=0) 318 319 self.batch_by_txn_request_handler.handle( 320 "Connection_1", before_message.SerializeToString()) 321 322 # If we cannot respond to the request, broadcast batch request and add 323 # to pending request 324 self.assert_message_was_broadcasted( 325 after_message, 326 validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST 327 ) 328 self.assert_request_pending( 329 requested_id="123", connection_id="Connection_1") 330 self.assert_message_not_sent(connection_id="Connection_1") 331 332 # Add the batch to the completer and resend the 333 # BatchByTransactionIdRequest 334 message = network_pb2.GossipBatchByTransactionIdRequest( 335 ids=["123"], 336 nonce="2", 337 time_to_live=1) 338 transaction = transaction_pb2.Transaction(header_signature="123") 339 batch = batch_pb2.Batch( 340 header_signature="abc", transactions=[transaction]) 341 self.completer.add_batch(batch) 342 self.batch_request_handler.handle( 343 "Connection_1", message.SerializeToString()) 344 345 # Check that the a Batch Response was sent back to "Connection_1" 346 self.assert_message_sent( 347 connection_id="Connection_1", 348 message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE 349 ) 350 351 def test_batch_by_transaction_id_response_handler_requested(self): 352 """ 353 Test that the BatchByTransactionIdResponderHandler correctly broadcasts 354 a received request that the Responder cannot respond to, and does not 355 rebroadcast the same request again. If we have already recieved the 356 request, do nothing. 357 """ 358 # The completer does not have the requested batch with the transaction 359 before_message = network_pb2.GossipBatchByTransactionIdRequest( 360 ids=["123"], 361 time_to_live=1) 362 363 after_message = network_pb2.GossipBatchByTransactionIdRequest( 364 ids=["123"], 365 time_to_live=0) 366 367 self.batch_by_txn_request_handler.handle( 368 "Connection_1", before_message.SerializeToString()) 369 370 # If we cannot respond to the request, broadcast batch request and add 371 # to pending request 372 self.assert_message_was_broadcasted( 373 after_message, 374 validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST 375 ) 376 self.assert_request_pending( 377 requested_id="123", connection_id="Connection_1") 378 self.assert_message_not_sent(connection_id="Connection_1") 379 380 self.gossip.clear() 381 382 # Message should be dropped since the same message has already been 383 # handled 384 self.batch_by_txn_request_handler.handle( 385 "Connection_2", before_message.SerializeToString()) 386 387 self.assert_message_was_not_broadcasted( 388 after_message, 389 validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST 390 ) 391 392 self.assert_request_not_pending( 393 requested_id="123", connection_id="Connection_2") 394 395 message = network_pb2.GossipBatchByTransactionIdRequest( 396 ids=["123"], 397 nonce="2", 398 time_to_live=1) 399 self.batch_by_txn_request_handler.handle( 400 "Connection_2", message.SerializeToString()) 401 402 self.assert_message_was_not_broadcasted( 403 message, 404 validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST 405 ) 406 self.assert_request_pending( 407 requested_id="123", connection_id="Connection_2") 408 self.assert_message_not_sent(connection_id="Connection_2") 409 410 def test_batch_by_transaction_id_multiple_txn_ids(self): 411 """ 412 Test that the BatchByTransactionIdResponderHandler correctly broadcasts 413 a new request with only the transaction_ids that the Responder cannot 414 respond to, and sends a GossipBatchResponse for the transactions_id 415 requests that can be satisfied. 416 """ 417 # Add batch that has txn 123 418 transaction = transaction_pb2.Transaction(header_signature="123") 419 batch = batch_pb2.Batch( 420 header_signature="abc", transactions=[transaction]) 421 self.completer.add_batch(batch) 422 # Request transactions 123 and 456 423 message = network_pb2.GossipBatchByTransactionIdRequest( 424 ids=["123", "456"], 425 time_to_live=1) 426 self.batch_by_txn_request_handler.handle( 427 "Connection_1", message.SerializeToString()) 428 self.batch_request_handler.handle( 429 "Connection_1", message.SerializeToString()) 430 431 # Respond with a BatchResponse for transaction 123 432 self.assert_message_sent( 433 connection_id="Connection_1", 434 message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE 435 ) 436 437 # Broadcast a BatchByTransactionIdRequest for just 456 438 after_message = \ 439 network_pb2.GossipBatchByTransactionIdRequest( 440 ids=["456"], 441 time_to_live=0) 442 443 self.assert_message_was_broadcasted( 444 after_message, 445 validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) 446 447 # And set a pending request for 456 448 self.assert_request_pending( 449 requested_id="456", connection_id="Connection_1") 450 451 def test_responder_batch_response_handler(self): 452 """ 453 Test that the ResponderBatchResponseHandler, after receiving a Batch 454 Response, checks to see if the responder has any pending request for 455 that batch and forwards the response on to the connection_id that 456 had requested it. 457 """ 458 # The Responder does not have any pending requests for block "ABC" 459 batch = batch_pb2.Batch(header_signature="abc") 460 461 response_message = network_pb2.GossipBatchResponse( 462 content=batch.SerializeToString()) 463 464 self.batch_response_handler.handle( 465 "Connection_1", (batch, response_message.SerializeToString())) 466 467 # ResponderBlockResponseHandler should not send any messages. 468 self.assert_message_not_sent("Connection_1") 469 self.assert_request_not_pending(requested_id="abc") 470 471 # Handle a request message for batch "abc". This adds it to the pending 472 # request queue. 473 request_message = \ 474 network_pb2.GossipBatchByBatchIdRequest(id="abc", time_to_live=1) 475 476 self.batch_request_handler.handle( 477 "Connection_2", request_message.SerializeToString()) 478 479 self.assert_request_pending( 480 requested_id="abc", connection_id="Connection_2") 481 482 # Handle the the BatchResponse Message. Since Connection_2 had 483 # requested the batch but it could not be fulfilled at that time of the 484 # request the received BatchResponse is forwarded to Connection_2 485 self.batch_response_handler.handle( 486 "Connection_1", (batch, response_message.SerializeToString())) 487 488 self.assert_message_sent( 489 connection_id="Connection_2", 490 message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE 491 ) 492 # The request for batch "abc" from "Connection_2" is no longer pending 493 # it should be removed from the pending request cache. 494 self.assert_request_not_pending(requested_id="abc") 495 496 def test_responder_batch_response_txn_handler(self): 497 """ 498 Test that the ResponderBatchResponseHandler, after receiving a Batch 499 Response, checks to see if the responder has any pending request for 500 that transactions in the batch and forwards the response on to the 501 connection_id that had them. 502 """ 503 transaction = transaction_pb2.Transaction(header_signature="123") 504 batch = batch_pb2.Batch( 505 header_signature="abc", transactions=[transaction]) 506 507 response_message = network_pb2.GossipBatchResponse( 508 content=batch.SerializeToString()) 509 510 request_message = \ 511 network_pb2.GossipBatchByTransactionIdRequest( 512 ids=["123"], 513 time_to_live=1) 514 515 # Send BatchByTransaciontIdRequest for txn "123" and add it to the 516 # pending request cache 517 self.batch_request_handler.handle( 518 "Connection_2", request_message.SerializeToString()) 519 520 self.assert_request_pending( 521 requested_id="123", connection_id="Connection_2") 522 523 # Send Batch Response that contains the batch that has txn "123" 524 self.batch_response_handler.handle( 525 "Connection_1", (batch, response_message.SerializeToString())) 526 527 # Handle the the BatchResponse Message. Since Connection_2 had 528 # requested the txn_id in the batch but it could not be fulfilled at 529 # that time of the request the received BatchResponse is forwarded to 530 # Connection_2 531 self.assert_message_sent( 532 connection_id="Connection_2", 533 message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE 534 ) 535 # The request for transaction_id "123" from "Connection_2" is no 536 # longer pending it should be removed from the pending request cache. 537 self.assert_request_not_pending(requested_id="123") 538 539 # assertions 540 def assert_message_was_broadcasted(self, message, message_type): 541 self.assertIn(message, self.gossip.broadcasted[message_type]) 542 543 def assert_message_was_not_broadcasted(self, message, message_type): 544 if message_type in self.gossip.broadcasted: 545 self.assertNotIn(message, self.gossip.broadcasted[message_type]) 546 else: 547 self.assertIsNone(self.gossip.broadcasted.get(message_type)) 548 549 def assert_message_not_sent(self, connection_id): 550 self.assertIsNone(self.gossip.sent.get(connection_id)) 551 552 def assert_message_sent(self, connection_id, message_type): 553 self.assertIsNotNone(self.gossip.sent.get(connection_id)) 554 self.assertTrue( 555 self.gossip.sent.get(connection_id)[0][0] == message_type) 556 557 def assert_request_pending(self, requested_id, connection_id): 558 self.assertIn(connection_id, self.responder.get_request(requested_id)) 559 560 def assert_request_not_pending(self, requested_id, connection_id=None): 561 if self.responder.get_request(requested_id) is not None: 562 self.assertFalse( 563 connection_id in self.responder.get_request(requested_id)) 564 else: 565 self.assertIsNone(self.responder.get_request(requested_id))