def do_batch_by_transaction_id_multiple_txn_ids(): transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) testResponder.completer.add_batch(batch) # Request transactions 123 and 456 message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123", "456"], time_to_live=1) testResponder.batch_by_txn_request_handler.handle( "Connection_1", message.SerializeToString()) testResponder.batch_request_handler.handle("Connection_1", message.SerializeToString()) # Respond with a BatchResponse for transaction 123 testResponder.assert_message_sent( connection_id="Connection_1", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE) # Broadcast a BatchByTransactionIdRequest for just 456 after_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["456"], time_to_live=0) testResponder.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) # And set a pending request for 456 testResponder.assert_request_pending(requested_id="456", connection_id="Connection_1")
def do_batch_by_transaction_id_response_handler(): before_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], nonce="1", time_to_live=1) after_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], nonce="1", time_to_live=0) testResponder.batch_by_txn_request_handler.handle( "Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast batch request and add # to pending request testResponder.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) testResponder.assert_request_pending(requested_id="123", connection_id="Connection_1") testResponder.assert_message_not_sent(connection_id="Connection_1") # Add the batch to the completer and resend the # BatchByTransactionIdRequest message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"], nonce="2", time_to_live=1) transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) testResponder.completer.add_batch(batch) testResponder.batch_request_handler.handle("Connection_1", message.SerializeToString()) # Check that the a Batch Response was sent back to "Connection_1" testResponder.assert_message_sent( connection_id="Connection_1", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)
def test_batch_by_transaction_id_multiple_txn_ids(): """ Test that the BatchByTransactionIdResponderHandler correctly broadcasts a new request with only the transaction_ids that the Responder cannot respond to, and sends a GossipBatchResponse for the transactions_id requests that can be satisfied. """ # Add batch that has txn 123 testResponder = TestResponder() transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) testResponder.completer.add_batch(batch) # Request transactions 123 and 456 message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123", "456"], time_to_live=1) testResponder.batch_by_txn_request_handler.handle( "Connection_1", message.SerializeToString()) testResponder.batch_request_handler.handle("Connection_1", message.SerializeToString()) # Respond with a BatchResponse for transaction 123 # Broadcast a BatchByTransactionIdRequest for just 456 after_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["456"], time_to_live=0)
def test_batch_by_transaction_id_response_handler_requested(): """ Test that the BatchByTransactionIdResponderHandler correctly broadcasts a received request that the Responder cannot respond to, and does not rebroadcast the same request again. If we have already recieved the request, do nothing. """ # The completer does not have the requested batch with the transaction testResponder = TestResponder() before_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=1) after_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=0) testResponder.batch_by_txn_request_handler.handle( "Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast batch request and add # to pending request testResponder.gossip.clear() # Message should be dropped since the same message has already been # handled testResponder.batch_by_txn_request_handler.handle( "Connection_2", before_message.SerializeToString()) message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"], nonce="2", time_to_live=1) testResponder.batch_by_txn_request_handler.handle( "Connection_2", message.SerializeToString())
def test_batch_by_transaction_id_response_handler(): """ Test that the BatchByTransactionIdResponderHandler correctly broadcasts a received request that the Responder cannot respond to, or sends a GossipBatchResponse back to the connection_id the handler received the request from. """ # The completer does not have the requested batch with the transaction testResponder = TestResponder() before_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], nonce="1", time_to_live=1) after_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], nonce="1", time_to_live=0) testResponder.batch_by_txn_request_handler.handle( "Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast batch request and add # to pending request # BatchByTransactionIdRequest message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"], nonce="2", time_to_live=1) transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) testResponder.completer.add_batch(batch) testResponder.batch_request_handler.handle("Connection_1", message.SerializeToString())
def handle(self, connection_id, message_content): batch_request_message = network_pb2.GossipBatchByTransactionIdRequest() batch_request_message.ParseFromString(message_content) node_id = batch_request_message.node_id batch = None batches = [] unfound_txn_ids = [] for txn_id in batch_request_message.ids: batch = self._responder.check_for_batch_by_transaction( txn_id) # The txn_id was not found. if batch is None: unfound_txn_ids.append(txn_id) # Check to see if a previous txn was in the same batch. elif batch not in batches: batches.append(batch) batch = None if batches == []: for txn_id in batch_request_message.ids: self._responder.add_request(txn_id, connection_id) self._gossip.broadcast( batch_request_message, validator_pb2.Message. GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, exclude=[connection_id]) elif unfound_txn_ids != []: new_request = network_pb2.GossipBatchByTransactionIdRequest() new_request.ids.extend(unfound_txn_ids) new_request.node_id = batch_request_message.node_id for txn_id in unfound_txn_ids: self._responder.add_request(txn_id, connection_id) self._gossip.broadcast( new_request, validator_pb2.Message. GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, exclude=[connection_id]) if batches != []: for batch in batches: LOGGER.debug("Responding to batch requests %s", batch.header_signature) batch_response = network_pb2.GossipBatchResponse( content=batch.SerializeToString(), node_id=node_id) self._gossip.send(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, batch_response.SerializeToString(), connection_id) return HandlerResult(status=HandlerStatus.PASS)
def test_batch_by_transaction_id_response_handler_requested(self): """ Test that the BatchByTransactionIdResponderHandler correctly broadcasts a received request that the Responder cannot respond to, and does not rebroadcast the same request again. If we have already recieved the request, do nothing. """ # The completer does not have the requested batch with the transaction before_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=1) after_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=0) self.batch_by_txn_request_handler.handle( "Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast batch request and add # to pending request self.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) self.assert_request_pending(requested_id="123", connection_id="Connection_1") self.assert_message_not_sent(connection_id="Connection_1") self.gossip.clear() # Message should be dropped since the same message has already been # handled self.batch_by_txn_request_handler.handle( "Connection_2", before_message.SerializeToString()) self.assert_message_was_not_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) self.assert_request_not_pending(requested_id="123", connection_id="Connection_2") message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"], nonce="2", time_to_live=1) self.batch_by_txn_request_handler.handle("Connection_2", message.SerializeToString()) self.assert_message_was_not_broadcasted( message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) self.assert_request_pending(requested_id="123", connection_id="Connection_2") self.assert_message_not_sent(connection_id="Connection_2")
def do_responder_batch_response_txn_handler(): transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) response_message = network_pb2.GossipBatchResponse( content=batch.SerializeToString()) request_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=1) # Send BatchByTransaciontIdRequest for txn "123" and add it to the # pending request cache testResponder.batch_request_handler.handle( "Connection_2", request_message.SerializeToString()) testResponder.assert_request_pending(requested_id="123", connection_id="Connection_2") # Send Batch Response that contains the batch that has txn "123" testResponder.batch_response_handler.handle( "Connection_1", (batch, response_message.SerializeToString())) # Handle the the BatchResponse Message. Since Connection_2 had # requested the txn_id in the batch but it could not be fulfilled at # that time of the request the received BatchResponse is forwarded to # Connection_2 testResponder.assert_message_sent( connection_id="Connection_2", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE) # The request for transaction_id "123" from "Connection_2" is no # longer pending it should be removed from the pending request cache. testResponder.assert_request_not_pending(requested_id="123")
def test_batch_by_transaction_id_response_handler(self): """ Test that the BatchByTransactionIdResponderHandler correctly broadcasts a received request that the Responder cannot respond to, or sends a GossipBatchResponse back to the connection_id the handler received the request from. """ # The completer does not have the requested batch with the transaction message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"], node_id=b"1") self.batch_by_txn_request_handler.handle("Connection_1", message.SerializeToString()) # If we cannot respond to the request, broadcast batch request and add # to pending request self.assert_message_was_broadcasted( message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) self.assert_request_pending(requested_id="123", connection_id="Connection_1") self.assert_message_not_sent(connection_id="Connection_1") # Add the batch to the completer and resend the # BatchByTransactionIdRequest transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) self.completer.add_batch(batch) self.batch_request_handler.handle("Connection_1", message.SerializeToString()) # Check that the a Batch Response was sent back to "Connection_1" self.assert_message_sent( connection_id="Connection_1", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)
def test_responder_batch_response_txn_handler(): """ Test that the ResponderBatchResponseHandler, after receiving a Batch Response, checks to see if the responder has any pending request for that transactions in the batch and forwards the response on to the connection_id that had them. """ testResponder = TestResponder() transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) response_message = network_pb2.GossipBatchResponse( content=batch.SerializeToString()) request_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=1) # Send BatchByTransaciontIdRequest for txn "123" and add it to the # pending request cache testResponder.batch_request_handler.handle( "Connection_2", request_message.SerializeToString()) # Send Batch Response that contains the batch that has txn "123" testResponder.batch_response_handler.handle( "Connection_1", (batch, response_message.SerializeToString()))
def do_batch_by_transaction_id_response_handler_requested(): before_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=1) after_message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], time_to_live=0) testResponder.batch_by_txn_request_handler.handle( "Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast batch request and add # to pending request testResponder.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) testResponder.assert_request_pending(requested_id="123", connection_id="Connection_1") testResponder.assert_message_not_sent(connection_id="Connection_1") testResponder.gossip.clear() # Message should be dropped since the same message has already been # handled testResponder.batch_by_txn_request_handler.handle( "Connection_2", before_message.SerializeToString()) testResponder.assert_message_was_not_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) testResponder.assert_request_not_pending(requested_id="123", connection_id="Connection_2") message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"], nonce="2", time_to_live=1) testResponder.batch_by_txn_request_handler.handle( "Connection_2", message.SerializeToString()) testResponder.assert_message_was_not_broadcasted( message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) testResponder.assert_request_pending(requested_id="123", connection_id="Connection_2") testResponder.assert_message_not_sent(connection_id="Connection_2")
def handle(self, identity, message_content): gossip_message = network_pb2.GossipBatchByTransactionIdRequest() gossip_message.ParseFromString(message_content) batch = None batches = [] unfound_txn_ids = [] for txn_id in gossip_message.ids: batch = self._responder.check_for_batch_by_transaction( txn_id) # The txn_id was not found. if batch is None: unfound_txn_ids.append(txn_id) # Check to see if a previous txn was in the same batch. elif batch not in batches: batches.append(batch) batch = None if batches == []: self._gossip.broadcast( gossip_message, validator_pb2.Message. GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) elif unfound_txn_ids != []: new_request = network_pb2.GossipBatchByTransactionIdRequest() new_request.ids.extend(unfound_txn_ids) new_request.node_id = gossip_message.node_id self._gossip.broadcast( new_request, validator_pb2.Message. GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) if batches != []: for batch in batches: LOGGER.debug("Responding to batch requests %s", batch.header_signature) self._gossip.broadcast_batch(batch) return HandlerResult( status=HandlerStatus.PASS)
def test_batch_by_transaction_id_multiple_txn_ids(self): """ Test that the BatchByTransactionIdResponderHandler correctly broadcasts a new request with only the transaction_ids that the Responder cannot respond to, and sends a GossipBatchResponse for the transactions_id requests that can be satisfied. """ # Add batch that has txn 123 transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction]) self.completer.add_batch(batch) # Request transactions 123 and 456 message = network_pb2.GossipBatchByTransactionIdRequest( ids=["123", "456"], time_to_live=1) self.batch_by_txn_request_handler.handle("Connection_1", message.SerializeToString()) self.batch_request_handler.handle("Connection_1", message.SerializeToString()) # Respond with a BatchResponse for transaction 123 self.assert_message_sent( connection_id="Connection_1", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE) # Broadcast a BatchByTransactionIdRequest for just 456 after_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["456"], time_to_live=0) self.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST) # And set a pending request for 456 self.assert_request_pending(requested_id="456", connection_id="Connection_1")
def test_responder_batch_response_txn_handler(self): """ Test that the ResponderBatchResponseHandler, after receiving a Batch Response, checks to see if the responder has any pending request for that transactions in the batch and forwards the response on to the connection_id that had them. """ transaction = transaction_pb2.Transaction(header_signature="123") batch = batch_pb2.Batch( header_signature="abc", transactions=[transaction]) response_message = network_pb2.GossipBatchResponse( content=batch.SerializeToString()) request_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["123"]) # Send BatchByTransaciontIdRequest for txn "123" and add it to the # pending request cache self.batch_request_handler.handle( "Connection_2", request_message.SerializeToString()) self.assert_request_pending( requested_id="123", connection_id="Connection_2") # Send Batch Response that contains the batch that has txn "123" self.batch_response_handler.handle( "Connection_1", response_message.SerializeToString()) # Handle the the BatchResponse Message. Since Connection_2 had # requested the txn_id in the batch but it could not be fulfilled at # that time of the request the received BatchResponse is forwarded to # Connection_2 self.assert_message_sent( connection_id="Connection_2", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE ) # The request for transaction_id "123" from "Connection_2" is no # longer pending it should be removed from the pending request cache. self.assert_request_not_pending(requested_id="123")
def handle(self, connection_id, message_content): batch_request_message = network_pb2.GossipBatchByTransactionIdRequest() batch_request_message.ParseFromString(message_content) if batch_request_message.nonce in self._seen_requests: LOGGER.debug( "Received repeat GossipBatchByTransactionIdRequest" " from %s", connection_id) return HandlerResult(HandlerStatus.DROP) batch = None batches = [] unfound_txn_ids = [] not_requested = [] for txn_id in batch_request_message.ids: batch = self._responder.check_for_batch_by_transaction(txn_id) # The txn_id was not found. if batch is None: unfound_txn_ids.append(txn_id) if not self._responder.already_requested(txn_id): not_requested.append(txn_id) else: LOGGER.debug( "Batch containing Transaction %s has already " "been requested", txn_id) # Check to see if a previous txn was in the same batch. elif batch not in batches: batches.append(batch) batch = None if batches == [] and len(not_requested) == \ len(batch_request_message.ids): if batch_request_message.time_to_live > 0: time_to_live = batch_request_message.time_to_live batch_request_message.time_to_live = time_to_live - 1 self._gossip.broadcast(batch_request_message, validator_pb2.Message. GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, exclude=[connection_id]) self._seen_requests[batch_request_message.nonce] = \ batch_request_message.ids for txn_id in batch_request_message.ids: self._responder.add_request(txn_id, connection_id) elif unfound_txn_ids != []: if not_requested != []: if batch_request_message.time_to_live > 0: self._seen_requests[batch_request_message.nonce] = \ batch_request_message.ids new_request = \ network_pb2.GossipBatchByTransactionIdRequest() # only request batches we have not requested already new_request.ids.extend(not_requested) # Keep same nonce as original message new_request.nonce = batch_request_message.nonce time_to_live = batch_request_message.time_to_live new_request.time_to_live = time_to_live - 1 self._gossip.broadcast( new_request, validator_pb2.Message. GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, exclude=[connection_id]) # Add all requests to responder for txn_id in unfound_txn_ids: self._responder.add_request(txn_id, connection_id) else: # Add all requests to responder for txn_id in unfound_txn_ids: self._responder.add_request(txn_id, connection_id) if batches != []: for batch in batches: LOGGER.debug("Responding to batch requests %s", batch.header_signature) batch_response = network_pb2.GossipBatchResponse( content=batch.SerializeToString(), ) self._gossip.send(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, batch_response.SerializeToString(), connection_id) return HandlerResult(HandlerStatus.PASS)