Пример #1
0
    def handle(self, connection_id, message_content):
        set_request = state_context_pb2.TpStateSetRequest()
        set_request.ParseFromString(message_content)
        set_values_list = [{e.address: e.data} for e in set_request.entries]
        try:
            return_value = self._context_manager.set(set_request.context_id,
                                                     set_values_list)
        except AuthorizationException:
            response = \
                state_context_pb2.TpStateSetResponse(
                    status=state_context_pb2.
                    TpStateSetResponse.AUTHORIZATION_ERROR)
            return HandlerResult(HandlerStatus.RETURN, response,
                                 validator_pb2.Message.TP_STATE_SET_RESPONSE)

        response = state_context_pb2.TpStateSetResponse(
            status=state_context_pb2.TpStateSetResponse.OK)
        if return_value is True:
            address_list = [e.address for e in set_request.entries]
            response.addresses.extend(address_list)
        else:
            LOGGER.debug("SET: No Values Set")
            response.addresses.extend([])
        return HandlerResult(HandlerStatus.RETURN, response,
                             validator_pb2.Message.TP_STATE_SET_RESPONSE)
Пример #2
0
    def handle(self, connection_id, message_content):
        if not self._can_accept():
            if not self._applying_backpressure:
                self._applying_backpressure = True
                LOGGER.info(
                    'Applying back pressure on client submitted batches: '
                    'current depth: %s, limit: %s', *self._queue_info())

            self._batches_rejected_count.inc()
            self._batches_rejected_gauge.set_value(
                self._batches_rejected_gauge.get_value() + 1)

            response = ClientBatchSubmitResponse(
                status=ClientBatchSubmitResponse.QUEUE_FULL)
            return HandlerResult(
                status=HandlerStatus.RETURN,
                message_out=response,
                message_type=Message.CLIENT_BATCH_SUBMIT_RESPONSE)
        else:
            if self._applying_backpressure:
                self._applying_backpressure = False
                self._batches_rejected_gauge.set_value(0)
                LOGGER.info(
                    'Ending back pressure on client submitted batches: '
                    'current depth: %s, limit: %s', *self._queue_info())

        return HandlerResult(status=HandlerStatus.PASS)
Пример #3
0
    def handle(self, connection_id, message_content):
        gossip_message = GossipMessage()
        gossip_message.ParseFromString(message_content)
        if gossip_message.content_type == gossip_message.BLOCK:
            block = Block()
            block.ParseFromString(gossip_message.content)
            has_block = False
            if self._completer.get_block(block.header_signature) is not None:
                has_block = True

            if not has_block and self._has_block(block.header_signature):
                has_block = True

            if has_block:
                return HandlerResult(HandlerStatus.DROP)

        if gossip_message.content_type == gossip_message.BATCH:
            batch = Batch()
            batch.ParseFromString(gossip_message.content)
            has_batch = False
            if self._completer.get_batch(batch.header_signature) is not None:
                has_batch = True

            if not has_batch and self._has_batch(batch.header_signature):
                has_batch = True

            if has_batch:
                return HandlerResult(HandlerStatus.DROP)

        return HandlerResult(HandlerStatus.PASS)
Пример #4
0
    def handle(self, connection_id, message_content):
        get_request = state_context_pb2.TpStateGetRequest()
        get_request.ParseFromString(message_content)
        try:
            return_values = self._context_manager.get(
                get_request.context_id, list(get_request.addresses))
        except AuthorizationException:
            response = \
                state_context_pb2.TpStateGetResponse(
                    status=state_context_pb2.
                    TpStateGetResponse.AUTHORIZATION_ERROR)
            return HandlerResult(HandlerStatus.RETURN, response,
                                 validator_pb2.Message.TP_STATE_GET_RESPONSE)

        return_list = return_values if return_values is not None else []

        entry_list = [
            state_context_pb2.TpStateEntry(address=a, data=d)
            for a, d in return_list
        ]
        response = state_context_pb2.TpStateGetResponse(
            status=state_context_pb2.TpStateGetResponse.OK)
        response.entries.extend(entry_list)
        return HandlerResult(HandlerStatus.RETURN, response,
                             validator_pb2.Message.TP_STATE_GET_RESPONSE)
Пример #5
0
    def handle(self, connection_id, message_content):
        block_response = network_pb2.GossipBlockResponse()
        block_response.ParseFromString(message_content)
        block = block_pb2.Block()
        block.ParseFromString(block_response.content)
        open_request = self._responder.get_request(block.header_signature)

        if open_request is None:
            return HandlerResult(status=HandlerStatus.PASS)

        for connection in open_request:
            LOGGER.debug("Responding to block request: Send %s to %s",
                         block.header_signature,
                         connection)
            try:
                self._gossip.send(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE,
                                  message_content,
                                  connection)
            except ValueError:
                LOGGER.debug("Can't send block response %s to closed "
                             "connection %s",
                             block.header_signature,
                             connection)

        self._responder.remove_request(block.header_signature)

        return HandlerResult(HandlerStatus.PASS)
Пример #6
0
    def handle(self, connection_id, message_content):
        # check to see if there is a public key
        public_key = self._network.connection_id_to_public_key(connection_id)

        if public_key is None:
            LOGGER.debug(
                "No public key found, %s is not permitted. "
                "Close connection.", connection_id)
            violation = AuthorizationViolation(
                violation=RoleType.Value("NETWORK"))
            self._gossip.unregister_peer(connection_id)
            return HandlerResult(
                HandlerStatus.RETURN_AND_CLOSE,
                message_out=violation,
                message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION)

        # check public key against network/default role
        permitted = self._permission_verifier.check_network_role(public_key)

        if not permitted:
            LOGGER.debug("Public key not permitted, %s is not permitted",
                         connection_id)
            self._gossip.unregister_peer(connection_id)
            violation = AuthorizationViolation(
                violation=RoleType.Value("NETWORK"))
            return HandlerResult(
                HandlerStatus.RETURN_AND_CLOSE,
                message_out=violation,
                message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION)

        # if allowed pass message
        return HandlerResult(HandlerStatus.PASS)
Пример #7
0
    def handle(self, connection_id, message_content):
        message = GossipMessage()
        message.ParseFromString(message_content)
        if message.content_type == GossipMessage.BLOCK:
            public_key = \
                self._network.connection_id_to_public_key(connection_id)
            block = Block()
            block.ParseFromString(message.content)
            header = BlockHeader()
            header.ParseFromString(block.header)
            if header.signer_public_key == public_key:
                permitted = \
                    self._permission_verifier.check_network_consensus_role(
                        public_key)
                if not permitted:
                    LOGGER.debug(
                        "Public key is not permitted to publish block, "
                        "remove connection: %s", connection_id)
                    self._gossip.unregister_peer(connection_id)
                    violation = AuthorizationViolation(
                        violation=RoleType.Value("NETWORK"))
                    return HandlerResult(HandlerStatus.RETURN_AND_CLOSE,
                                         message_out=violation,
                                         message_type=validator_pb2.Message.
                                         AUTHORIZATION_VIOLATION)

        # if allowed pass message
        return HandlerResult(HandlerStatus.PASS)
Пример #8
0
    def handle(self, connection_id, message_content):
        exclude = [connection_id]
        gossip_message = GossipMessage()
        gossip_message.ParseFromString(message_content)
        if gossip_message.time_to_live == 0:
            # Do not forward message if it has reached its time to live limit
            return HandlerResult(status=HandlerStatus.PASS)

        else:
            # decrement time_to_live
            time_to_live = gossip_message.time_to_live
            gossip_message.time_to_live = time_to_live - 1

        if gossip_message.content_type == GossipMessage.BATCH:
            batch = Batch()
            batch.ParseFromString(gossip_message.content)
            # If we already have this batch, don't forward it
            if not self._completer.get_batch(batch.header_signature):
                self._gossip.broadcast_batch(batch, exclude)
        elif gossip_message.content_type == GossipMessage.BLOCK:
            block = Block()
            block.ParseFromString(gossip_message.content)
            # If we already have this block, don't forward it
            if not self._completer.get_block(block.header_signature):
                self._gossip.broadcast_block(block, exclude)
        else:
            LOGGER.info("received %s, not BATCH or BLOCK",
                        gossip_message.content_type)
        return HandlerResult(status=HandlerStatus.PASS)
Пример #9
0
    def handle(self, connection_id, message_content):
        batch_response_message = GossipBatchResponse()
        batch_response_message.ParseFromString(message_content)

        batch = Batch()
        batch.ParseFromString(batch_response_message.content)
        if not is_valid_batch(batch):
            LOGGER.debug("requested batch's structure is invalid: %s",
                         batch.header_signature)
            return HandlerResult(status=HandlerStatus.DROP)

        return HandlerResult(status=HandlerStatus.PASS)
Пример #10
0
    def handle(self, connection_id, message_content):
        block_response_message = GossipBlockResponse()
        block_response_message.ParseFromString(message_content)

        block = Block()
        block.ParseFromString(block_response_message.content)
        if not is_valid_block(block):
            LOGGER.debug("requested block's batches structure is invalid: %s",
                         block.header_signature)
            return HandlerResult(status=HandlerStatus.DROP)

        return HandlerResult(status=HandlerStatus.PASS)
Пример #11
0
    def handle(self, connection_id, message_content):
        block_request_message = network_pb2.GossipBlockRequest()
        block_request_message.ParseFromString(message_content)
        if block_request_message.nonce in self._seen_requests:
            LOGGER.debug("Received repeat GossipBlockRequest from %s",
                         connection_id)

            return HandlerResult(HandlerStatus.DROP)

        block_id = block_request_message.block_id
        block = self._responder.check_for_block(block_id)
        if block is None:
            # No block found, broadcast original message to other peers
            # and add to pending requests
            if block_id == "HEAD":
                LOGGER.debug("No chain head available. Cannot respond to block"
                             " requests.")
            else:
                if not self._responder.already_requested(block_id):
                    if block_request_message.time_to_live > 0:
                        time_to_live = block_request_message.time_to_live
                        block_request_message.time_to_live = time_to_live - 1
                        self._gossip.broadcast(
                            block_request_message,
                            validator_pb2.Message.GOSSIP_BLOCK_REQUEST,
                            exclude=[connection_id])

                        self._seen_requests[block_request_message.nonce] = \
                            block_request_message.block_id

                        self._responder.add_request(block_id, connection_id)
                else:
                    LOGGER.debug("Block %s has already been requested",
                                 block_id)

                    self._responder.add_request(block_id, connection_id)
        else:
            LOGGER.debug("Responding to block requests: %s",
                         block.get_block().header_signature)

            block_response = network_pb2.GossipBlockResponse(
                content=block.get_block().SerializeToString())

            self._gossip.send(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE,
                              block_response.SerializeToString(),
                              connection_id)

        return HandlerResult(HandlerStatus.PASS)
Пример #12
0
    def handle(self, connection_id, message_content):
        batch_request_message = network_pb2.GossipBatchByBatchIdRequest()
        batch_request_message.ParseFromString(message_content)
        if batch_request_message.nonce in self._seen_requests:
            LOGGER.debug("Received repeat GossipBatchByBatchIdRequest from %s",
                         connection_id)
            return HandlerResult(HandlerStatus.DROP)

        batch = None
        batch = self._responder.check_for_batch(batch_request_message.id)

        if batch is None:
            # No batch found, broadcast original message to other peers
            # and add to pending requests
            if not self._responder.already_requested(batch_request_message.id):

                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_BATCH_ID_REQUEST,
                        exclude=[connection_id])

                    self._seen_requests[batch_request_message.nonce] = \
                        batch_request_message.id

                    self._responder.add_request(batch_request_message.id,
                                                connection_id)
            else:
                LOGGER.debug("Batch %s has already been requested",
                             batch_request_message.id)

                self._responder.add_request(batch_request_message.id,
                                            connection_id)
        else:
            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)
Пример #13
0
    def handle(self, connection_id, message_content):
        block_response_message = GossipBlockResponse()
        block_response_message.ParseFromString(message_content)
        block = Block()
        block.ParseFromString(block_response_message.content)
        if block.header_signature in self._seen_cache:
            self.block_dropped_count.inc()
            return HandlerResult(status=HandlerStatus.DROP)

        if not is_valid_block(block):
            LOGGER.debug("requested block's signature is invalid: %s",
                         block.header_signature)
            return HandlerResult(status=HandlerStatus.DROP)

        self._seen_cache = TimedCache()
        return HandlerResult(status=HandlerStatus.PASS)
Пример #14
0
    def handle(self, connection_id, message_content):
        batch_response = network_pb2.GossipBatchResponse()
        batch_response.ParseFromString(message_content)
        batch = batch_pb2.Batch()
        batch.ParseFromString(batch_response.content)
        open_request = self._responder.get_request(batch.header_signature)

        if open_request is None:
            open_request = []

        requests_to_remove = [batch.header_signature]
        for txn in batch.transactions:
            requests_by_txn = self._responder.get_request(txn.header_signature)
            if requests_by_txn is not None:
                open_request += requests_by_txn
                requests_to_remove += [txn.header_signature]

        for connection in open_request:
            LOGGER.debug("Responding to batch requests: Send %s to %s",
                         batch.header_signature,
                         connection)
            try:
                self._gossip.send(validator_pb2.Message.GOSSIP_BATCH_RESPONSE,
                                  message_content,
                                  connection)
            except ValueError:
                LOGGER.debug("Can't send batch response %s to closed "
                             "connection %s",
                             batch.header_signature,
                             connection)

        for requested_id in requests_to_remove:
            self._responder.remove_request(requested_id)

        return HandlerResult(HandlerStatus.PASS)
Пример #15
0
    def handle(self, connection_id, message_content):
        request = processor_pb2.TpRegisterRequest()
        request.ParseFromString(message_content)
        if request.max_occupancy == 0:
            max_occupancy = DEFAULT_MAX_OCCUPANCY
            LOGGER.warning(
                'Max occupancy was not provided by transaction processor: %s.'
                ' Using default max occupancy: %s', connection_id,
                DEFAULT_MAX_OCCUPANCY)
        else:
            max_occupancy = request.max_occupancy

        LOGGER.info(
            'registered transaction processor: connection_id=%s, family=%s, '
            'version=%s, namespaces=%s, max_occupancy=%s',
            connection_id, request.family, request.version,
            list(request.namespaces), max_occupancy)

        processor_type = processor_manager.ProcessorType(
            request.family, request.version)

        processor = processor_manager.Processor(connection_id,
                                                request.namespaces,
                                                max_occupancy)

        self._collection[processor_type] = processor

        ack = processor_pb2.TpRegisterResponse()
        ack.status = ack.OK

        return HandlerResult(
            status=HandlerStatus.RETURN,
            message_out=ack,
            message_type=validator_pb2.Message.TP_REGISTER_RESPONSE)
Пример #16
0
 def _network_violation_result():
     violation = AuthorizationViolation(
         violation=RoleType.Value("NETWORK"))
     return HandlerResult(
         HandlerStatus.RETURN_AND_CLOSE,
         message_out=violation,
         message_type=validator_pb2.Message.AUTHORIZATION_VIOLATION)
Пример #17
0
    def handle(self, connection_id, message_content):
        response_proto = client_batch_submit_pb2.ClientBatchSubmitResponse

        def make_response(out_status):
            return HandlerResult(
                status=HandlerStatus.RETURN,
                message_out=response_proto(status=out_status),
                message_type=Message.CLIENT_BATCH_SUBMIT_RESPONSE)

        try:
            request = client_batch_submit_pb2.ClientBatchSubmitRequest()
            request.ParseFromString(message_content)
            for batch in request.batches:
                if batch.trace:
                    LOGGER.debug("TRACE %s: %s", batch.header_signature,
                                 self.__class__.__name__)
            if not all(
                    self._verifier.check_off_chain_batch_roles(batch)
                    for batch in request.batches):
                return make_response(response_proto.INVALID_BATCH)

            if not all(
                    self._verifier.is_batch_signer_authorized(batch)
                    for batch in request.batches):
                return make_response(response_proto.INVALID_BATCH)

        except DecodeError:
            return make_response(response_proto.INTERNAL_ERROR)

        return HandlerResult(status=HandlerStatus.PASS)
Пример #18
0
    def handle(self, connection_id, message_content):
        request = ClientEventsSubscribeRequest()
        request.ParseFromString(message_content)

        ack = ClientEventsSubscribeResponse()
        try:
            subscriptions = [
                EventSubscription(
                    event_type=sub.event_type,
                    filters=[
                        self._filter_factory.create(f.key, f.match_string,
                                                    f.filter_type)
                        for f in sub.filters
                    ],
                ) for sub in request.subscriptions
            ]
        except InvalidFilterError as err:
            LOGGER.warning("Invalid Filter Error: %s", err)
            ack.status = ack.INVALID_FILTER
            ack.response_message = str(err)
            return HandlerResult(HandlerStatus.RETURN,
                                 message_out=ack,
                                 message_type=self._msg_type)

        last_known_block_ids = list(request.last_known_block_ids)

        last_known_block_id = None
        if last_known_block_ids:
            try:
                last_known_block_id = \
                    self._event_broadcaster.get_latest_known_block_id(
                        last_known_block_ids)
            except NoKnownBlockError as err:
                ack.status = ack.UNKNOWN_BLOCK
                ack.response_message = str(err)
                return HandlerResult(HandlerStatus.RETURN,
                                     message_out=ack,
                                     message_type=self._msg_type)

        self._event_broadcaster.add_subscriber(connection_id, subscriptions,
                                               last_known_block_id)

        ack.status = ack.OK
        return HandlerResult(HandlerStatus.RETURN_AND_PASS,
                             message_out=ack,
                             message_type=self._msg_type)
Пример #19
0
    def handle(self, connection_id, message_content):
        batch_response_message = GossipBatchResponse()
        batch_response_message.ParseFromString(message_content)

        batch = Batch()
        batch.ParseFromString(batch_response_message.content)
        if batch.header_signature in self._seen_cache:
            self._batch_dropped_count.inc()
            return HandlerResult(status=HandlerStatus.DROP)

        if not is_valid_batch(batch):
            LOGGER.debug("requested batch's signature is invalid: %s",
                         batch.header_signature)
            return HandlerResult(status=HandlerStatus.DROP)

        self._seen_cache[batch.header_signature] = None
        return HandlerResult(status=HandlerStatus.PASS)
Пример #20
0
    def handle(self, connection_id, message_content):
        # Attempt to catch the subscriber up with events
        try:
            self._event_broadcaster.catchup_subscriber(connection_id)
        except (PossibleForkDetectedError, NoKnownBlockError, KeyError) as err:
            LOGGER.warning("Failed to catchup subscriber: %s", err)

        self._event_broadcaster.enable_subscriber(connection_id)
        return HandlerResult(HandlerStatus.PASS)
Пример #21
0
    def handle(self, connection_id, message_content):
        block_response_message = network_pb2.GossipBlockResponse()
        block_response_message.ParseFromString(message_content)

        block = Block()
        block.ParseFromString(block_response_message.content)
        self._completer.add_block(block)

        return HandlerResult(status=HandlerStatus.PASS)
Пример #22
0
    def handle(self, connection_id, message_content):
        batch_response_message = network_pb2.GossipBatchResponse()
        batch_response_message.ParseFromString(message_content)

        batch = Batch()
        batch.ParseFromString(batch_response_message.content)
        self._completer.add_batch(batch)

        return HandlerResult(status=HandlerStatus.PASS)
Пример #23
0
 def handle(self, connection_id, message_content):
     request = ClientBatchSubmitRequest()
     request.ParseFromString(message_content)
     for batch in request.batches:
         if batch.trace:
             LOGGER.debug("TRACE %s: %s", batch.header_signature,
                          self.__class__.__name__)
         self._completer.add_batch(batch)
         self._gossip.broadcast_batch(batch)
     return HandlerResult(status=HandlerStatus.PASS)
Пример #24
0
    def handle(self, connection_id, message_content):
        delete_request = state_context_pb2.TpStateDeleteRequest()
        delete_request.ParseFromString(message_content)
        try:
            self._context_manager.delete(delete_request.context_id,
                                         list(delete_request.addresses))
        except AuthorizationException:
            response = \
                state_context_pb2.TpStateDeleteResponse(
                    status=state_context_pb2.
                    TpStateDeleteResponse.AUTHORIZATION_ERROR)
            return HandlerResult(
                HandlerStatus.RETURN, response,
                validator_pb2.Message.TP_STATE_DELETE_RESPONSE)

        response = state_context_pb2.TpStateDeleteResponse(
            addresses=delete_request.addresses,
            status=state_context_pb2.TpStateDeleteResponse.OK)
        return HandlerResult(HandlerStatus.RETURN, response,
                             validator_pb2.Message.TP_STATE_DELETE_RESPONSE)
Пример #25
0
 def handle(self, connection_id, message_content):
     gossip_message = network_pb2.GossipMessage()
     gossip_message.ParseFromString(message_content)
     if gossip_message.content_type == network_pb2.GossipMessage.BLOCK:
         block = Block()
         block.ParseFromString(gossip_message.content)
         self._completer.add_block(block)
     elif gossip_message.content_type == network_pb2.GossipMessage.BATCH:
         batch = Batch()
         batch.ParseFromString(gossip_message.content)
         self._completer.add_batch(batch)
     return HandlerResult(status=HandlerStatus.PASS)
Пример #26
0
    def handle(self, connection_id, message_content):
        response = GetPeersResponse()
        response.ParseFromString(message_content)

        LOGGER.debug(
            "Got peers response message from %s. Endpoints: %s",
            connection_id,
            response.peer_endpoints)

        self._gossip.add_candidate_peer_endpoints(response.peer_endpoints)

        return HandlerResult(HandlerStatus.PASS)
Пример #27
0
 def handle(self, connection_id, message_content):
     """
     If an AuthorizationViolation is recieved, the connection has decided
     that this validator is no longer permitted to be connected.
     Remove the connection preemptively.
     """
     LOGGER.warning("Received AuthorizationViolation from %s",
                    connection_id)
     # Close the connection
     endpoint = self._network.connection_id_to_endpoint(connection_id)
     self._network.remove_connection(connection_id)
     self._gossip.remove_temp_endpoint(endpoint)
     return HandlerResult(HandlerStatus.DROP)
Пример #28
0
    def handle(self, connection_id, message_content):
        request = ClientEventsGetRequest()
        request.ParseFromString(message_content)

        resp = ClientEventsGetResponse()
        try:
            subscriptions = [
                EventSubscription(
                    event_type=sub.event_type,
                    filters=[
                        self._filter_factory.create(f.key, f.match_string,
                                                    f.filter_type)
                        for f in sub.filters
                    ],
                ) for sub in request.subscriptions
            ]
        except InvalidFilterError as err:
            LOGGER.warning("Invalid Filter Error: %s", err)
            resp.status = resp.INVALID_FILTER
            return HandlerResult(
                HandlerStatus.RETURN,
                message_out=resp,
                message_type=validator_pb2.Message.CLIENT_EVENTS_GET_RESPONSE)
        try:
            events = self._event_broadcaster.get_events_for_block_ids(
                request.block_ids, subscriptions)
        except KeyError:
            resp.status = resp.UNKNOWN_BLOCK
            return HandlerResult(
                HandlerStatus.RETURN,
                message_out=resp,
                message_type=validator_pb2.Message.CLIENT_EVENTS_GET_RESPONSE)

        resp.events.extend(events)
        resp.status = resp.OK
        return HandlerResult(
            HandlerStatus.RETURN,
            message_out=resp,
            message_type=validator_pb2.Message.CLIENT_EVENTS_GET_RESPONSE)
Пример #29
0
    def _wrap_result(self, response):
        """Wraps child's response in a HandlerResult to be sent back to client.

        Args:
            response (enum or dict): Either an integer status enum, or a dict
                of attributes to be added to the protobuf response.
        """
        if isinstance(response, int):
            response = self._wrap_response(response)

        return HandlerResult(status=HandlerStatus.RETURN,
                             message_out=self._response_proto(**response),
                             message_type=self._response_type)
Пример #30
0
    def handle(self, connection_id, message_content):
        batch_response_message = GossipBatchResponse()
        batch_response_message.ParseFromString(message_content)
        batch = Batch()
        batch.ParseFromString(batch_response_message.content)

        batch_id = batch.header_signature

        ack = NetworkAcknowledgement()
        ack.status = ack.OK

        if not self._has_open_requests(batch_id) and self._has_batch(batch_id):
            return HandlerResult(
                HandlerStatus.RETURN,
                message_out=ack,
                message_type=validator_pb2.Message.NETWORK_ACK
            )

        return HandlerResult(
            HandlerStatus.RETURN_AND_PASS,
            message_out=ack,
            message_type=validator_pb2.Message.NETWORK_ACK)