def setUp(self): self.gossip = MockGossip() self.completer = MockCompleter() self.responder = Responder(self.completer) self.block_request_handler = \ BlockResponderHandler(self.responder, self.gossip) self.block_response_handler = \ ResponderBlockResponseHandler(self.responder, self.gossip) self.batch_request_handler = \ BatchByBatchIdResponderHandler(self.responder, self.gossip) self.batch_response_handler = \ ResponderBatchResponseHandler(self.responder, self.gossip) self.batch_by_txn_request_handler = \ BatchByTransactionIdResponderHandler(self.responder, self.gossip)
def __init__(self, network_endpoint, component_endpoint, peer_list, data_dir, identity_signing_key): """Constructs a validator instance. Args: network_endpoint (str): the network endpoint component_endpoint (str): the component endpoint peer_list (list of str): a list of peer addresses data_dir (str): path to the data directory key_dir (str): path to the key directory """ db_filename = os.path.join( data_dir, 'merkle-{}.lmdb'.format(network_endpoint[-2:])) LOGGER.debug('database file is %s', db_filename) merkle_db = LMDBNoLockDatabase(db_filename, 'c') context_manager = ContextManager(merkle_db) self._context_manager = context_manager state_view_factory = StateViewFactory(merkle_db) block_db_filename = os.path.join( data_dir, 'block-{}.lmdb'.format(network_endpoint[-2:])) LOGGER.debug('block store file is %s', block_db_filename) block_db = LMDBNoLockDatabase(block_db_filename, 'c') block_store = BlockStore(block_db) # setup network self._dispatcher = Dispatcher() thread_pool = ThreadPoolExecutor(max_workers=10) process_pool = ProcessPoolExecutor(max_workers=3) self._thread_pool = thread_pool self._process_pool = process_pool self._service = Interconnect(component_endpoint, self._dispatcher, secured=False, heartbeat=False, max_incoming_connections=20) executor = TransactionExecutor(service=self._service, context_manager=context_manager, config_view_factory=ConfigViewFactory( StateViewFactory(merkle_db))) self._executor = executor zmq_identity = hashlib.sha512( time.time().hex().encode()).hexdigest()[:23] network_thread_pool = ThreadPoolExecutor(max_workers=10) self._network_thread_pool = network_thread_pool self._network_dispatcher = Dispatcher() # Server public and private keys are hardcoded here due to # the decision to avoid having separate identities for each # validator's server socket. This is appropriate for a public # network. For a permissioned network with requirements for # server endpoint authentication at the network level, this can # be augmented with a local lookup service for side-band provided # endpoint, public_key pairs and a local configuration option # for 'server' side private keys. self._network = Interconnect( network_endpoint, dispatcher=self._network_dispatcher, zmq_identity=zmq_identity, secured=True, server_public_key=b'wFMwoOt>yFqI/ek.G[tfMMILHWw#vXB[Sv}>l>i)', server_private_key=b'r&oJ5aQDj4+V]p2:Lz70Eu0x#m%IwzBdP(}&hWM*', heartbeat=True, connection_timeout=30, max_incoming_connections=100) self._gossip = Gossip(self._network, initial_peer_endpoints=peer_list) completer = Completer(block_store, self._gossip) block_sender = BroadcastBlockSender(completer, self._gossip) batch_sender = BroadcastBatchSender(completer, self._gossip) chain_id_manager = ChainIdManager(data_dir) # Create and configure journal self._journal = Journal( block_store=block_store, state_view_factory=StateViewFactory(merkle_db), block_sender=block_sender, batch_sender=batch_sender, transaction_executor=executor, squash_handler=context_manager.get_squash_handler(), identity_signing_key=identity_signing_key, chain_id_manager=chain_id_manager, data_dir=data_dir, check_publish_block_frequency=0.1, block_cache_purge_frequency=30, block_cache_keep_time=300) self._genesis_controller = GenesisController( context_manager=context_manager, transaction_executor=executor, completer=completer, block_store=block_store, state_view_factory=state_view_factory, identity_key=identity_signing_key, data_dir=data_dir, chain_id_manager=chain_id_manager, batch_sender=batch_sender) responder = Responder(completer) completer.set_on_batch_received(self._journal.on_batch_received) completer.set_on_block_received(self._journal.on_block_received) self._dispatcher.add_handler( validator_pb2.Message.TP_STATE_GET_REQUEST, tp_state_handlers.TpStateGetHandler(context_manager), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_STATE_SET_REQUEST, tp_state_handlers.TpStateSetHandler(context_manager), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_REGISTER_REQUEST, processor_handlers.ProcessorRegisterHandler(executor.processors), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_UNREGISTER_REQUEST, processor_handlers.ProcessorUnRegisterHandler(executor.processors), thread_pool) # Set up base network handlers self._network_dispatcher.add_handler( validator_pb2.Message.NETWORK_PING, PingHandler(), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.NETWORK_CONNECT, ConnectHandler(network=self._network), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.NETWORK_DISCONNECT, DisconnectHandler(network=self._network), network_thread_pool) # Set up gossip handlers self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_REGISTER, PeerRegisterHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_UNREGISTER, PeerUnregisterHandler(gossip=self._gossip), network_thread_pool) # GOSSIP_MESSAGE 1) Sends acknowledgement to the sender self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipMessageHandler(), network_thread_pool) # GOSSIP_MESSAGE 2) Verifies signature self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, signature_verifier.GossipMessageSignatureVerifier(), process_pool) # GOSSIP_MESSAGE 3) Determines if we should broadcast the # message to our peers. It is important that this occur prior # to the sending of the message to the completer, as this step # relies on whether the gossip message has previously been # seen by the validator to determine whether or not forwarding # should occur self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipBroadcastHandler(gossip=self._gossip, completer=completer), network_thread_pool) # GOSSIP_MESSAGE 4) Send message to completer self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, CompleterGossipHandler(completer), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_REQUEST, BlockResponderHandler(responder, self._gossip), network_thread_pool) # GOSSIP_BLOCK_RESPONSE 1) Sends ack to the sender self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, GossipBlockResponseHandler(), network_thread_pool) # GOSSIP_BLOCK_RESPONSE 2) Verifies signature self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, signature_verifier.GossipBlockResponseSignatureVerifier(), process_pool) # GOSSIP_BLOCK_RESPONSE 3) Send message to completer self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, CompleterGossipBlockResponseHandler(completer), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, BatchByBatchIdResponderHandler(responder, self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, BatchByTransactionIdResponderHandler(responder, self._gossip), network_thread_pool) # GOSSIP_BATCH_RESPONSE 1) Sends ack to the sender self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, GossipBatchResponseHandler(), network_thread_pool) # GOSSIP_BATCH_RESPONSE 2) Verifies signature self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, signature_verifier.GossipBatchResponseSignatureVerifier(), process_pool) # GOSSIP_BATCH_RESPONSE 3) Send message to completer self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, CompleterGossipBatchResponseHandler(completer), network_thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, signature_verifier.BatchListSignatureVerifier(), process_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, CompleterBatchListBroadcastHandler(completer, self._gossip), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, client_handlers.BatchSubmitFinisher( self._journal.get_block_store(), completer.batch_cache), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_STATUS_REQUEST, client_handlers.BatchStatusRequest(self._journal.get_block_store(), completer.batch_cache), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_LIST_REQUEST, client_handlers.StateListRequest(merkle_db, self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_GET_REQUEST, client_handlers.StateGetRequest(merkle_db, self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BLOCK_LIST_REQUEST, client_handlers.BlockListRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BLOCK_GET_REQUEST, client_handlers.BlockGetRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_LIST_REQUEST, client_handlers.BatchListRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_GET_REQUEST, client_handlers.BatchGetRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_CURRENT_REQUEST, client_handlers.StateCurrentRequest( self._journal.get_current_root), thread_pool)
def __init__(self, bind_network, bind_component, endpoint, peering, seeds_list, peer_list, data_dir, config_dir, identity_signing_key, scheduler_type, network_public_key=None, network_private_key=None): """Constructs a validator instance. Args: bind_network (str): the network endpoint bind_component (str): the component endpoint endpoint (str): the zmq-style URI of this validator's publically reachable endpoint peering (str): The type of peering approach. Either 'static' or 'dynamic'. In 'static' mode, no attempted topology buildout occurs -- the validator only attempts to initiate peering connections with endpoints specified in the peer_list. In 'dynamic' mode, the validator will first attempt to initiate peering connections with endpoints specified in the peer_list and then attempt to do a topology buildout starting with peer lists obtained from endpoints in the seeds_list. In either mode, the validator will accept incoming peer requests up to max_peers. seeds_list (list of str): a list of addresses to connect to in order to perform the initial topology buildout peer_list (list of str): a list of peer addresses data_dir (str): path to the data directory config_dir (str): path to the config directory identity_signing_key (str): key validator uses for signing """ db_filename = os.path.join(data_dir, 'merkle-{}.lmdb'.format( bind_network[-2:])) LOGGER.debug('database file is %s', db_filename) merkle_db = LMDBNoLockDatabase(db_filename, 'c') delta_db_filename = os.path.join(data_dir, 'state-deltas-{}.lmdb'.format( bind_network[-2:])) LOGGER.debug('state delta store file is %s', delta_db_filename) state_delta_db = LMDBNoLockDatabase(delta_db_filename, 'c') state_delta_store = StateDeltaStore(state_delta_db) context_manager = ContextManager(merkle_db, state_delta_store) self._context_manager = context_manager state_view_factory = StateViewFactory(merkle_db) block_db_filename = os.path.join(data_dir, 'block-{}.lmdb'.format( bind_network[-2:])) LOGGER.debug('block store file is %s', block_db_filename) block_db = LMDBNoLockDatabase(block_db_filename, 'c') block_store = BlockStore(block_db) batch_tracker = BatchTracker(block_store) block_store.add_update_observer(batch_tracker) # setup network self._dispatcher = Dispatcher() thread_pool = ThreadPoolExecutor(max_workers=10) sig_pool = ThreadPoolExecutor(max_workers=3) self._thread_pool = thread_pool self._sig_pool = sig_pool self._service = Interconnect(bind_component, self._dispatcher, secured=False, heartbeat=False, max_incoming_connections=20, monitor=True) config_file = os.path.join(config_dir, "validator.toml") validator_config = {} if os.path.exists(config_file): with open(config_file) as fd: raw_config = fd.read() validator_config = toml.loads(raw_config) if scheduler_type is None: scheduler_type = validator_config.get("scheduler", "serial") executor = TransactionExecutor( service=self._service, context_manager=context_manager, settings_view_factory=SettingsViewFactory( StateViewFactory(merkle_db)), scheduler_type=scheduler_type, invalid_observers=[batch_tracker]) self._executor = executor self._service.set_check_connections(executor.check_connections) state_delta_processor = StateDeltaProcessor(self._service, state_delta_store, block_store) zmq_identity = hashlib.sha512( time.time().hex().encode()).hexdigest()[:23] network_thread_pool = ThreadPoolExecutor(max_workers=10) self._network_thread_pool = network_thread_pool self._network_dispatcher = Dispatcher() secure = False if network_public_key is not None and network_private_key is not None: secure = True self._network = Interconnect( bind_network, dispatcher=self._network_dispatcher, zmq_identity=zmq_identity, secured=secure, server_public_key=network_public_key, server_private_key=network_private_key, heartbeat=True, public_endpoint=endpoint, connection_timeout=30, max_incoming_connections=100) self._gossip = Gossip(self._network, endpoint=endpoint, peering_mode=peering, initial_seed_endpoints=seeds_list, initial_peer_endpoints=peer_list, minimum_peer_connectivity=3, maximum_peer_connectivity=10, topology_check_frequency=1) completer = Completer(block_store, self._gossip) block_sender = BroadcastBlockSender(completer, self._gossip) batch_sender = BroadcastBatchSender(completer, self._gossip) chain_id_manager = ChainIdManager(data_dir) # Create and configure journal self._journal = Journal( block_store=block_store, state_view_factory=StateViewFactory(merkle_db), block_sender=block_sender, batch_sender=batch_sender, transaction_executor=executor, squash_handler=context_manager.get_squash_handler(), identity_signing_key=identity_signing_key, chain_id_manager=chain_id_manager, state_delta_processor=state_delta_processor, data_dir=data_dir, config_dir=config_dir, check_publish_block_frequency=0.1, block_cache_purge_frequency=30, block_cache_keep_time=300, batch_observers=[batch_tracker] ) self._genesis_controller = GenesisController( context_manager=context_manager, transaction_executor=executor, completer=completer, block_store=block_store, state_view_factory=state_view_factory, identity_key=identity_signing_key, data_dir=data_dir, config_dir=config_dir, chain_id_manager=chain_id_manager, batch_sender=batch_sender ) responder = Responder(completer) completer.set_on_batch_received(self._journal.on_batch_received) completer.set_on_block_received(self._journal.on_block_received) self._dispatcher.add_handler( validator_pb2.Message.TP_STATE_GET_REQUEST, tp_state_handlers.TpStateGetHandler(context_manager), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_STATE_SET_REQUEST, tp_state_handlers.TpStateSetHandler(context_manager), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_REGISTER_REQUEST, processor_handlers.ProcessorRegisterHandler(executor.processors), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_UNREGISTER_REQUEST, processor_handlers.ProcessorUnRegisterHandler(executor.processors), thread_pool) # Set up base network handlers self._network_dispatcher.add_handler( validator_pb2.Message.NETWORK_PING, PingHandler(), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.NETWORK_CONNECT, ConnectHandler(network=self._network), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.NETWORK_DISCONNECT, DisconnectHandler(network=self._network), network_thread_pool) # Set up gossip handlers self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_GET_PEERS_REQUEST, GetPeersRequestHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_GET_PEERS_RESPONSE, GetPeersResponseHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_REGISTER, PeerRegisterHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_UNREGISTER, PeerUnregisterHandler(gossip=self._gossip), network_thread_pool) # GOSSIP_MESSAGE 1) Sends acknowledgement to the sender self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipMessageHandler(), network_thread_pool) # GOSSIP_MESSAGE 2) Verifies signature self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, signature_verifier.GossipMessageSignatureVerifier(), sig_pool) # GOSSIP_MESSAGE 3) Verifies batch structure self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, structure_verifier.GossipHandlerStructureVerifier(), network_thread_pool) # GOSSIP_MESSAGE 4) Determines if we should broadcast the # message to our peers. It is important that this occur prior # to the sending of the message to the completer, as this step # relies on whether the gossip message has previously been # seen by the validator to determine whether or not forwarding # should occur self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipBroadcastHandler( gossip=self._gossip, completer=completer), network_thread_pool) # GOSSIP_MESSAGE 5) Send message to completer self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, CompleterGossipHandler( completer), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_REQUEST, BlockResponderHandler(responder, self._gossip), network_thread_pool) # GOSSIP_BLOCK_RESPONSE 1) Sends ack to the sender self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, GossipBlockResponseHandler(), network_thread_pool) # GOSSIP_BLOCK_RESPONSE 2) Verifies signature self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, signature_verifier.GossipBlockResponseSignatureVerifier(), sig_pool) # GOSSIP_BLOCK_RESPONSE 3) Check batch structure self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, structure_verifier.GossipBlockResponseStructureVerifier(), network_thread_pool) # GOSSIP_BLOCK_RESPONSE 4) Send message to completer self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, CompleterGossipBlockResponseHandler( completer), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, ResponderBlockResponseHandler(responder, self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, BatchByBatchIdResponderHandler(responder, self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, BatchByTransactionIdResponderHandler(responder, self._gossip), network_thread_pool) # GOSSIP_BATCH_RESPONSE 1) Sends ack to the sender self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, GossipBatchResponseHandler(), network_thread_pool) # GOSSIP_BATCH_RESPONSE 2) Verifies signature self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, signature_verifier.GossipBatchResponseSignatureVerifier(), sig_pool) # GOSSIP_BATCH_RESPONSE 3) Check batch structure self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, structure_verifier.GossipBatchResponseStructureVerifier(), network_thread_pool) # GOSSIP_BATCH_RESPONSE 4) Send message to completer self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, CompleterGossipBatchResponseHandler( completer), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, ResponderBatchResponseHandler(responder, self._gossip), network_thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, BatchListPermissionVerifier( settings_view_factory=SettingsViewFactory( StateViewFactory(merkle_db)), current_root_func=self._journal.get_current_root, ), sig_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, signature_verifier.BatchListSignatureVerifier(), sig_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, structure_verifier.BatchListStructureVerifier(), network_thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, CompleterBatchListBroadcastHandler( completer, self._gossip), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, client_handlers.BatchSubmitFinisher(batch_tracker), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_STATUS_REQUEST, client_handlers.BatchStatusRequest(batch_tracker), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_LIST_REQUEST, client_handlers.StateListRequest( merkle_db, self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_GET_REQUEST, client_handlers.StateGetRequest( merkle_db, self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BLOCK_LIST_REQUEST, client_handlers.BlockListRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BLOCK_GET_REQUEST, client_handlers.BlockGetRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_LIST_REQUEST, client_handlers.BatchListRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_GET_REQUEST, client_handlers.BatchGetRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_TRANSACTION_LIST_REQUEST, client_handlers.TransactionListRequest( self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_TRANSACTION_GET_REQUEST, client_handlers.TransactionGetRequest( self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_CURRENT_REQUEST, client_handlers.StateCurrentRequest( self._journal.get_current_root), thread_pool) # State Delta Subscription Handlers self._dispatcher.add_handler( validator_pb2.Message.STATE_DELTA_SUBSCRIBE_REQUEST, StateDeltaSubscriberValidationHandler(state_delta_processor), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.STATE_DELTA_SUBSCRIBE_REQUEST, StateDeltaAddSubscriberHandler(state_delta_processor), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.STATE_DELTA_UNSUBSCRIBE_REQUEST, StateDeltaUnsubscriberHandler(state_delta_processor), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.STATE_DELTA_GET_EVENTS_REQUEST, StateDeltaGetEventsHandler(block_store, state_delta_store), thread_pool)
class TestResponder(unittest.TestCase): def setUp(self): self.gossip = MockGossip() self.completer = MockCompleter() self.responder = Responder(self.completer) self.block_request_handler = \ BlockResponderHandler(self.responder, self.gossip) self.block_response_handler = \ ResponderBlockResponseHandler(self.responder, self.gossip) self.batch_request_handler = \ BatchByBatchIdResponderHandler(self.responder, self.gossip) self.batch_response_handler = \ ResponderBatchResponseHandler(self.responder, self.gossip) self.batch_by_txn_request_handler = \ BatchByTransactionIdResponderHandler(self.responder, self.gossip) # Tests def test_block_responder_handler(self): """ Test that the BlockResponderHandler correctly broadcasts a received request that the Responder cannot respond to, or sends a GossipBlockResponse back to the connection_id the handler received the request from. """ # The completer does not have the requested block before_message = network_pb2.GossipBlockRequest(block_id="ABC", nonce="1", time_to_live=1) after_message = network_pb2.GossipBlockRequest(block_id="ABC", nonce="1", time_to_live=0) self.block_request_handler.handle("Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast the block request # and add to pending request self.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) self.assert_request_pending(requested_id="ABC", connection_id="Connection_1") self.assert_message_not_sent(connection_id="Connection_1") # Add the block to the completer and resend the Block Request block = block_pb2.Block(header_signature="ABC") self.completer.add_block(block) message = network_pb2.GossipBlockRequest(block_id="ABC", nonce="2", time_to_live=1) self.block_request_handler.handle("Connection_1", message.SerializeToString()) # Check that the a Block Response was sent back to "Connection_1" self.assert_message_sent( connection_id="Connection_1", message_type=validator_pb2.Message.GOSSIP_BLOCK_RESPONSE) def test_block_responder_handler_requested(self): """ Test that the BlockResponderHandler correctly broadcasts a received request that the Responder cannot respond to, and does not rebroadcast the same request. If we have already recieved the request, do nothing. """ before_message = network_pb2.GossipBlockRequest(block_id="ABC", nonce="1", time_to_live=1) after_message = network_pb2.GossipBlockRequest(block_id="ABC", nonce="1", time_to_live=0) self.block_request_handler.handle("Connection_1", before_message.SerializeToString()) # If we cannot respond to the request, broadcast the block request # and add to pending request self.assert_message_was_broadcasted( after_message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) self.assert_request_pending(requested_id="ABC", 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.block_request_handler.handle("Connection_2", before_message.SerializeToString()) self.assert_message_was_not_broadcasted( before_message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) self.assert_request_not_pending(requested_id="ABC", connection_id="Connection_2") message = network_pb2.GossipBlockRequest(block_id="ABC", nonce="2", time_to_live=1) self.block_request_handler.handle("Connection_2", message.SerializeToString()) self.assert_message_was_not_broadcasted( message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) self.assert_request_pending(requested_id="ABC", connection_id="Connection_2") self.assert_message_not_sent(connection_id="Connection_2") def test_responder_block_response_handler(self): """ Test that the ResponderBlockResponseHandler, after receiving a Block Response, checks to see if the responder has any pending request for that response and forwards the response on to the connection_id that had requested it. """ # The Responder does not have any pending requests for block "ABC" block = block_pb2.Block(header_signature="ABC") response_message = network_pb2.GossipBlockResponse( content=block.SerializeToString()) self.block_response_handler.handle( "Connection_1", response_message.SerializeToString()) # ResponderBlockResponseHandler should not send any messages. self.assert_message_not_sent("Connection_1") self.assert_request_not_pending(requested_id="ABC") # Handle a request message for block "ABC". This adds it to the pending # request queue. request_message = \ network_pb2.GossipBlockRequest(block_id="ABC", time_to_live=1) self.block_request_handler.handle("Connection_2", request_message.SerializeToString()) self.assert_request_pending(requested_id="ABC", connection_id="Connection_2") # Handle the the BlockResponse Message. Since Connection_2 had # requested the block but it could not be fulfilled at that time of the # request the received BlockResponse is forwarded to Connection_2 self.block_response_handler.handle( "Connection_1", response_message.SerializeToString()) self.assert_message_sent( connection_id="Connection_2", message_type=validator_pb2.Message.GOSSIP_BLOCK_RESPONSE) # The request for block "ABC" from "Connection_2" is no longer pending # it should be removed from the pending request cache. self.assert_request_not_pending(requested_id="ABC") def test_batch_by_id_responder_handler(self): """ Test that the BatchByBatchIdResponderHandler 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 before_message = network_pb2.GossipBatchByBatchIdRequest( id="abc", nonce="1", time_to_live=1) after_message = network_pb2.GossipBatchByBatchIdRequest(id="abc", nonce="1", time_to_live=0) self.batch_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_BATCH_ID_REQUEST) self.assert_request_pending(requested_id="abc", connection_id="Connection_1") self.assert_message_not_sent(connection_id="Connection_1") # Add the batch to the completer and resend the BatchByBatchIdRequest message = network_pb2.GossipBatchByBatchIdRequest(id="abc", nonce="2", time_to_live=1) batch = batch_pb2.Batch(header_signature="abc") 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_batch_by_id_responder_handler_requested(self): """ Test that the BatchByBatchIdResponderHandler 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 before_message = network_pb2.GossipBatchByBatchIdRequest( id="abc", nonce="1", time_to_live=1) after_message = network_pb2.GossipBatchByBatchIdRequest(id="abc", nonce="1", time_to_live=0) self.batch_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_BATCH_ID_REQUEST) self.assert_request_pending(requested_id="abc", 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_request_handler.handle("Connection_2", before_message.SerializeToString()) self.assert_message_was_not_broadcasted( before_message, validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST) self.assert_request_not_pending(requested_id="abc", connection_id="Connection_2") message = network_pb2.GossipBatchByBatchIdRequest(id="abc", nonce="2", time_to_live=1) self.batch_request_handler.handle("Connection_2", message.SerializeToString()) self.assert_message_was_not_broadcasted( message, validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST) self.assert_request_pending(requested_id="abc", connection_id="Connection_2") self.assert_message_not_sent(connection_id="Connection_2") 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 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) 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") # 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]) 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_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 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_handler(self): """ Test that the ResponderBatchResponseHandler, after receiving a Batch Response, checks to see if the responder has any pending request for that batch and forwards the response on to the connection_id that had requested it. """ # The Responder does not have any pending requests for block "ABC" batch = batch_pb2.Batch(header_signature="abc") response_message = network_pb2.GossipBatchResponse( content=batch.SerializeToString()) self.batch_response_handler.handle( "Connection_1", response_message.SerializeToString()) # ResponderBlockResponseHandler should not send any messages. self.assert_message_not_sent("Connection_1") self.assert_request_not_pending(requested_id="abc") # Handle a request message for batch "abc". This adds it to the pending # request queue. request_message = \ network_pb2.GossipBatchByBatchIdRequest(id="abc", time_to_live=1) self.batch_request_handler.handle("Connection_2", request_message.SerializeToString()) self.assert_request_pending(requested_id="abc", connection_id="Connection_2") # Handle the the BatchResponse Message. Since Connection_2 had # requested the batch but it could not be fulfilled at that time of the # request the received BatchResponse is forwarded to Connection_2 self.batch_response_handler.handle( "Connection_1", response_message.SerializeToString()) self.assert_message_sent( connection_id="Connection_2", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE) # The request for batch "abc" from "Connection_2" is no longer pending # it should be removed from the pending request cache. self.assert_request_not_pending(requested_id="abc") 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"], time_to_live=1) # 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") # assertions def assert_message_was_broadcasted(self, message, message_type): self.assertIn(message, self.gossip.broadcasted[message_type]) def assert_message_was_not_broadcasted(self, message, message_type): if message_type in self.gossip.broadcasted: self.assertNotIn(message, self.gossip.broadcasted[message_type]) else: self.assertIsNone(self.gossip.broadcasted.get(message_type)) def assert_message_not_sent(self, connection_id): self.assertIsNone(self.gossip.sent.get(connection_id)) def assert_message_sent(self, connection_id, message_type): self.assertIsNotNone(self.gossip.sent.get(connection_id)) self.assertTrue( self.gossip.sent.get(connection_id)[0][0] == message_type) def assert_request_pending(self, requested_id, connection_id): self.assertIn(connection_id, self.responder.get_request(requested_id)) def assert_request_not_pending(self, requested_id, connection_id=None): if self.responder.get_request(requested_id) is not None: self.assertFalse( connection_id in self.responder.get_request(requested_id)) else: self.assertIsNone(self.responder.get_request(requested_id))
def add( dispatcher, interconnect, gossip, completer, responder, thread_pool, sig_pool, has_block, has_batch, permission_verifier, consensus_notifier, ): # -- Basic Networking -- # dispatcher.add_handler(validator_pb2.Message.PING_REQUEST, PingRequestHandler(network=interconnect), thread_pool, priority=Priority.HIGH) dispatcher.add_handler(validator_pb2.Message.PING_RESPONSE, PingResponseHandler(), thread_pool) dispatcher.add_handler(validator_pb2.Message.NETWORK_CONNECT, ConnectHandler(network=interconnect), thread_pool) dispatcher.add_handler(validator_pb2.Message.NETWORK_DISCONNECT, DisconnectHandler(network=interconnect), thread_pool) # -- Authorization -- # dispatcher.set_message_priority( validator_pb2.Message.AUTHORIZATION_CONNECTION_RESPONSE, Priority.MEDIUM) dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_VIOLATION, AuthorizationViolationHandler(network=interconnect, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.AUTHORIZATION_TRUST_REQUEST, AuthorizationTrustRequestHandler( network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool, priority=Priority.MEDIUM) challenge_request_handler = AuthorizationChallengeRequestHandler( network=interconnect) dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_CHALLENGE_REQUEST, challenge_request_handler, thread_pool, priority=Priority.MEDIUM) dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_CHALLENGE_SUBMIT, AuthorizationChallengeSubmitHandler( network=interconnect, permission_verifier=permission_verifier, gossip=gossip, cache=challenge_request_handler.get_challenge_payload_cache()), thread_pool, priority=Priority.MEDIUM) # -- Gossip -- # dispatcher.add_handler( validator_pb2.Message.GOSSIP_GET_PEERS_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_GET_PEERS_REQUEST, GetPeersRequestHandler(gossip=gossip), thread_pool) dispatcher.set_message_priority( validator_pb2.Message.GOSSIP_GET_PEERS_REQUEST, Priority.MEDIUM) dispatcher.add_handler( validator_pb2.Message.GOSSIP_GET_PEERS_RESPONSE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_GET_PEERS_RESPONSE, GetPeersResponseHandler(gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_REGISTER, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_REGISTER, PeerRegisterHandler(gossip=gossip), thread_pool) dispatcher.set_message_priority(validator_pb2.Message.GOSSIP_REGISTER, Priority.MEDIUM) dispatcher.add_handler(validator_pb2.Message.GOSSIP_UNREGISTER, PeerUnregisterHandler(gossip=gossip), thread_pool) # GOSSIP_MESSAGE ) Check if this is a block and if we already have it dispatcher.set_preprocessor(validator_pb2.Message.GOSSIP_MESSAGE, gossip_message_preprocessor, thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, GossipMessageDuplicateHandler(), thread_pool) # GOSSIP_MESSAGE ) Verify Network Permissions dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_MESSAGE ) Verifies signature dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, signature_verifier.GossipMessageSignatureVerifier(), sig_pool) # GOSSIP_MESSAGE ) Verifies batch structure dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, structure_verifier.GossipHandlerStructureVerifier(), thread_pool) # GOSSIP_MESSAGE ) Verifies that the node is allowed to publish a # block dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, NetworkConsensusPermissionHandler( network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_MESSAGE ) Determines if this is a consensus message and notifies # the consensus engine if it is dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipConsensusHandler(gossip=gossip, notifier=consensus_notifier), thread_pool) # GOSSIP_MESSAGE ) Determines if we should broadcast the # message to our peers. It is important that this occur prior # to the sending of the message to the completer, as this step # relies on whether the gossip message has previously been # seen by the validator to determine whether or not forwarding # should occur dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, GossipBroadcastHandler(gossip=gossip), thread_pool) # GOSSIP_MESSAGE ) Send message to completer dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, CompleterGossipHandler(completer), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_REQUEST, BlockResponderHandler(responder, gossip), thread_pool) dispatcher.set_preprocessor(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, gossip_block_response_preprocessor, thread_pool) # GOSSIP_BLOCK_RESPONSE 1) Check for duplicate responses dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, GossipBlockResponseHandler(completer, responder), thread_pool) # GOSSIP_MESSAGE 2) Verify Network Permissions dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_BLOCK_RESPONSE 3) Verifies signature dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, signature_verifier.GossipBlockResponseSignatureVerifier(), sig_pool) # GOSSIP_BLOCK_RESPONSE 4) Check batch structure dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, structure_verifier.GossipBlockResponseStructureVerifier(), thread_pool) # GOSSIP_BLOCK_RESPONSE 5) Send message to completer dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, CompleterGossipBlockResponseHandler(completer), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, ResponderBlockResponseHandler(responder, gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, BatchByBatchIdResponderHandler(responder, gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, BatchByTransactionIdResponderHandler(responder, gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.set_preprocessor(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, gossip_batch_response_preprocessor, thread_pool) # GOSSIP_BATCH_RESPONSE 1) Check for duplicate responses dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, GossipBatchResponseHandler(completer, responder, has_batch), thread_pool) # GOSSIP_BATCH_RESPONSE 2) Verifies signature dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, signature_verifier.GossipBatchResponseSignatureVerifier(), sig_pool) # GOSSIP_BATCH_RESPONSE 3) Check batch structure dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, structure_verifier.GossipBatchResponseStructureVerifier(), thread_pool) # GOSSIP_BATCH_RESPONSE 4) Send message to completer dispatcher.add_handler(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, CompleterGossipBatchResponseHandler(completer), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, ResponderBatchResponseHandler(responder, gossip), thread_pool)
class TestResponder(unittest.TestCase): def setUp(self): self.gossip = MockGossip() self.completer = MockCompleter() self.responder = Responder(self.completer) self.block_request_handler = \ BlockResponderHandler(self.responder, self.gossip) self.block_response_handler = \ ResponderBlockResponseHandler(self.responder, self.gossip) self.batch_request_handler = \ BatchByBatchIdResponderHandler(self.responder, self.gossip) self.batch_response_handler = \ ResponderBatchResponseHandler(self.responder, self.gossip) self.batch_by_txn_request_handler = \ BatchByTransactionIdResponderHandler(self.responder, self.gossip) # Tests def test_block_responder_handler(self): """ Test that the BlockResponderHandler correctly broadcasts a received request that the Responder cannot respond to, or sends a GossipBlockResponse back to the connection_id the handler received the request from. """ # The completer does not have the requested block message = network_pb2.GossipBlockRequest(block_id="ABC", node_id=b"1") self.block_request_handler.handle( "Connection_1", message.SerializeToString()) # If we cannot respond to the request, broadcast the block request # and add to pending request self.assert_message_was_broadcasted( message, validator_pb2.Message.GOSSIP_BLOCK_REQUEST) self.assert_request_pending( requested_id="ABC", connection_id="Connection_1") self.assert_message_not_sent(connection_id="Connection_1") # Add the block to the completer and resend the Block Request block = block_pb2.Block(header_signature="ABC") self.completer.add_block(block) self.block_request_handler.handle( "Connection_1", message.SerializeToString()) # Check that the a Block Response was sent back to "Connection_1" self.assert_message_sent( connection_id="Connection_1", message_type=validator_pb2.Message.GOSSIP_BLOCK_RESPONSE ) def test_responder_block_response_handler(self): """ Test that the ResponderBlockResponseHandler, after receiving a Block Response, checks to see if the responder has any pending request for that response and forwards the response on to the connection_id that had requested it. """ # The Responder does not have any pending requests for block "ABC" block = block_pb2.Block(header_signature="ABC") response_message = network_pb2.GossipBlockResponse( content=block.SerializeToString(), node_id=b"1") self.block_response_handler.handle( "Connection_1", response_message.SerializeToString()) # ResponderBlockResponseHandler should not send any messages. self.assert_message_not_sent("Connection_1") self.assert_request_not_pending(requested_id="ABC") # Handle a request message for block "ABC". This adds it to the pending # request queue. request_message = \ network_pb2.GossipBlockRequest(block_id="ABC", node_id=b"1") self.block_request_handler.handle( "Connection_2", request_message.SerializeToString()) self.assert_request_pending( requested_id="ABC", connection_id="Connection_2") # Handle the the BlockResponse Message. Since Connection_2 had # requested the block but it could not be fulfilled at that time of the # request the received BlockResponse is forwarded to Connection_2 self.block_response_handler.handle( "Connection_1", response_message.SerializeToString()) self.assert_message_sent( connection_id="Connection_2", message_type=validator_pb2.Message.GOSSIP_BLOCK_RESPONSE ) # The request for block "ABC" from "Connection_2" is no longer pending # it should be removed from the pending request cache. self.assert_request_not_pending(requested_id="ABC") def test_batch_by_id_responder_handler(self): """ Test that the BatchByBatchIdResponderHandler 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 message = network_pb2.GossipBatchByBatchIdRequest( id="abc", node_id=b"1") self.batch_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_BATCH_ID_REQUEST) self.assert_request_pending( requested_id="abc", connection_id="Connection_1") self.assert_message_not_sent(connection_id="Connection_1") # Add the batch to the completer and resend the BatchByBatchIdRequest batch = batch_pb2.Batch(header_signature="abc") 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_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_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"], node_id=b"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 request_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["456"], node_id=b"1") self.assert_message_was_broadcasted( request_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_handler(self): """ Test that the ResponderBatchResponseHandler, after receiving a Batch Response, checks to see if the responder has any pending request for that batch and forwards the response on to the connection_id that had requested it. """ # The Responder does not have any pending requests for block "ABC" batch = batch_pb2.Batch(header_signature="abc") response_message = network_pb2.GossipBatchResponse( content=batch.SerializeToString(), node_id=b"1") self.batch_response_handler.handle( "Connection_1", response_message.SerializeToString()) # ResponderBlockResponseHandler should not send any messages. self.assert_message_not_sent("Connection_1") self.assert_request_not_pending(requested_id="abc") # Handle a request message for batch "abc". This adds it to the pending # request queue. request_message = \ network_pb2.GossipBatchByBatchIdRequest(id="abc", node_id=b"1") self.batch_request_handler.handle( "Connection_2", request_message.SerializeToString()) self.assert_request_pending( requested_id="abc", connection_id="Connection_2") # Handle the the BatchResponse Message. Since Connection_2 had # requested the batch but it could not be fulfilled at that time of the # request the received BatchResponse is forwarded to Connection_2 self.batch_response_handler.handle( "Connection_1", response_message.SerializeToString()) self.assert_message_sent( connection_id="Connection_2", message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE ) # The request for batch "abc" from "Connection_2" is no longer pending # it should be removed from the pending request cache. self.assert_request_not_pending(requested_id="abc") 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(), node_id=b"1") request_message = \ network_pb2.GossipBatchByTransactionIdRequest( ids=["123"], node_id=b"1") # 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") # assertions def assert_message_was_broadcasted(self, message, message_type): self.assertIn(message, self.gossip.broadcasted[message_type]) def assert_message_not_sent(self, connection_id): self.assertIsNone(self.gossip.sent.get(connection_id)) def assert_message_sent(self, connection_id, message_type): self.assertIsNotNone(self.gossip.sent.get(connection_id)) self.assertTrue(self.gossip.sent.get(connection_id)[0][0] == \ message_type) def assert_request_pending(self, requested_id, connection_id): self.assertIn(connection_id, self.responder.get_request(requested_id)) def assert_request_not_pending(self, requested_id): self.assertIsNone(self.responder.get_request(requested_id))
def __init__(self, network_endpoint, component_endpoint, peer_list, data_dir, key_dir): """Constructs a validator instance. Args: network_endpoint (str): the network endpoint component_endpoint (str): the component endpoint peer_list (list of str): a list of peer addresses data_dir (str): path to the data directory key_dir (str): path to the key directory """ db_filename = os.path.join( data_dir, 'merkle-{}.lmdb'.format(network_endpoint[-2:])) LOGGER.debug('database file is %s', db_filename) merkle_db = LMDBNoLockDatabase(db_filename, 'n') context_manager = ContextManager(merkle_db) state_view_factory = StateViewFactory(merkle_db) block_db_filename = os.path.join( data_dir, 'block-{}.lmdb'.format(network_endpoint[-2:])) LOGGER.debug('block store file is %s', block_db_filename) block_db = LMDBNoLockDatabase(block_db_filename, 'n') block_store = BlockStore(block_db) # setup network self._dispatcher = Dispatcher() thread_pool = ThreadPoolExecutor(max_workers=10) process_pool = ProcessPoolExecutor(max_workers=3) self._service = Interconnect(component_endpoint, self._dispatcher, secured=False) executor = TransactionExecutor(service=self._service, context_manager=context_manager, config_view_factory=ConfigViewFactory( StateViewFactory(merkle_db))) identity = hashlib.sha512(time.time().hex().encode()).hexdigest()[:23] identity_signing_key = Validator.load_identity_signing_key( key_dir, DEFAULT_KEY_NAME) network_thread_pool = ThreadPoolExecutor(max_workers=10) self._network_dispatcher = Dispatcher() # Server public and private keys are hardcoded here due to # the decision to avoid having separate identities for each # validator's server socket. This is appropriate for a public # network. For a permissioned network with requirements for # server endpoint authentication at the network level, this can # be augmented with a local lookup service for side-band provided # endpoint, public_key pairs and a local configuration option # for 'server' side private keys. self._network = Interconnect( network_endpoint, dispatcher=self._network_dispatcher, identity=identity, peer_connections=peer_list, secured=True, server_public_key=b'wFMwoOt>yFqI/ek.G[tfMMILHWw#vXB[Sv}>l>i)', server_private_key=b'r&oJ5aQDj4+V]p2:Lz70Eu0x#m%IwzBdP(}&hWM*') self._gossip = Gossip(self._network) completer = Completer(block_store, self._gossip) block_sender = BroadcastBlockSender(completer, self._gossip) chain_id_manager = ChainIdManager(data_dir) # Create and configure journal self._journal = Journal( block_store=block_store, state_view_factory=StateViewFactory(merkle_db), block_sender=block_sender, transaction_executor=executor, squash_handler=context_manager.get_squash_handler(), identity_signing_key=identity_signing_key, chain_id_manager=chain_id_manager) self._genesis_controller = GenesisController( context_manager=context_manager, transaction_executor=executor, completer=completer, block_store=block_store, state_view_factory=state_view_factory, identity_key=identity_signing_key, data_dir=data_dir, chain_id_manager=chain_id_manager) responder = Responder(completer) completer.set_on_batch_received(self._journal.on_batch_received) completer.set_on_block_received(self._journal.on_block_received) self._dispatcher.add_handler( validator_pb2.Message.TP_STATE_GET_REQUEST, tp_state_handlers.TpStateGetHandler(context_manager), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_STATE_SET_REQUEST, tp_state_handlers.TpStateSetHandler(context_manager), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_REGISTER_REQUEST, processor_handlers.ProcessorRegisterHandler(executor.processors), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.TP_UNREGISTER_REQUEST, processor_handlers.ProcessorUnRegisterHandler(executor.processors), thread_pool) self._network_dispatcher.add_handler(validator_pb2.Message.GOSSIP_PING, PingHandler(), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_REGISTER, PeerRegisterHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_UNREGISTER, PeerUnregisterHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipMessageHandler(), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, signature_verifier.GossipMessageSignatureVerifier(), process_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipBroadcastHandler(gossip=self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, CompleterGossipHandler(completer), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_REQUEST, BlockResponderHandler(responder, self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, BatchByBatchIdResponderHandler(responder, self._gossip), network_thread_pool) self._network_dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, BatchByTransactionIdResponderHandler(responder, self._gossip), network_thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, signature_verifier.BatchListSignatureVerifier(), process_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, CompleterBatchListBroadcastHandler(completer, self._gossip), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BATCH_STATUS_REQUEST, client_handlers.BatchStatusRequest(self._journal.get_block_store(), completer.batch_cache), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_LIST_REQUEST, client_handlers.StateListRequest(merkle_db, self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_GET_REQUEST, client_handlers.StateGetRequest(merkle_db, self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BLOCK_GET_REQUEST, client_handlers.BlockGetRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_BLOCK_LIST_REQUEST, client_handlers.BlockListRequest(self._journal.get_block_store()), thread_pool) self._dispatcher.add_handler( validator_pb2.Message.CLIENT_STATE_CURRENT_REQUEST, client_handlers.StateCurrentRequest( self._journal.get_current_root), thread_pool)
def add( dispatcher, interconnect, gossip, completer, responder, thread_pool, sig_pool, permission_verifier, ): # -- Basic Networking -- # dispatcher.add_handler(validator_pb2.Message.PING_REQUEST, PingHandler(network=interconnect), thread_pool) dispatcher.add_handler(validator_pb2.Message.NETWORK_CONNECT, ConnectHandler(network=interconnect), thread_pool) dispatcher.add_handler(validator_pb2.Message.NETWORK_DISCONNECT, DisconnectHandler(network=interconnect), thread_pool) # -- Authorization -- # dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_VIOLATION, AuthorizationViolationHandler(network=interconnect, gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_TRUST_REQUEST, AuthorizationTrustRequestHandler( network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_CHALLENGE_REQUEST, AuthorizationChallengeRequestHandler(network=interconnect), thread_pool) dispatcher.add_handler( validator_pb2.Message.AUTHORIZATION_CHALLENGE_SUBMIT, AuthorizationChallengeSubmitHandler( network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # -- Gossip -- # dispatcher.add_handler( validator_pb2.Message.GOSSIP_GET_PEERS_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_GET_PEERS_REQUEST, GetPeersRequestHandler(gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_GET_PEERS_RESPONSE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_GET_PEERS_RESPONSE, GetPeersResponseHandler(gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_REGISTER, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_REGISTER, PeerRegisterHandler(gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_UNREGISTER, PeerUnregisterHandler(gossip=gossip), thread_pool) # GOSSIP_MESSAGE 1) Sends acknowledgement to the sender dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, GossipMessageHandler(), thread_pool) # GOSSIP_MESSAGE 2) Verify Network Permissions dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_MESSAGE 3) Verifies signature dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, signature_verifier.GossipMessageSignatureVerifier(), sig_pool) # GOSSIP_MESSAGE 4) Verifies batch structure dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, structure_verifier.GossipHandlerStructureVerifier(), thread_pool) # GOSSIP_MESSAGE 4) Verifies that the node is allowed to publish a # block dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, NetworkConsensusPermissionHandler( network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_MESSAGE 5) Determines if we should broadcast the # message to our peers. It is important that this occur prior # to the sending of the message to the completer, as this step # relies on whether the gossip message has previously been # seen by the validator to determine whether or not forwarding # should occur dispatcher.add_handler( validator_pb2.Message.GOSSIP_MESSAGE, GossipBroadcastHandler(gossip=gossip, completer=completer), thread_pool) # GOSSIP_MESSAGE 6) Send message to completer dispatcher.add_handler(validator_pb2.Message.GOSSIP_MESSAGE, CompleterGossipHandler(completer), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_REQUEST, BlockResponderHandler(responder, gossip), thread_pool) # GOSSIP_BLOCK_RESPONSE 1) Sends ack to the sender dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, GossipBlockResponseHandler(), thread_pool) # GOSSIP_MESSAGE 2) Verify Network Permissions dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_BLOCK_RESPONSE 3) Verifies signature dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, signature_verifier.GossipBlockResponseSignatureVerifier(), sig_pool) # GOSSIP_BLOCK_RESPONSE 4) Check batch structure dispatcher.add_handler( validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, structure_verifier.GossipBlockResponseStructureVerifier(), thread_pool) # GOSSIP_BLOCK_RESPONSE 5) Send message to completer dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, CompleterGossipBlockResponseHandler(completer), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, ResponderBlockResponseHandler(responder, gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, BatchByBatchIdResponderHandler(responder, gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, BatchByTransactionIdResponderHandler(responder, gossip), thread_pool) dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, NetworkPermissionHandler(network=interconnect, permission_verifier=permission_verifier, gossip=gossip), thread_pool) # GOSSIP_BATCH_RESPONSE 1) Sends ack to the sender dispatcher.add_handler(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, GossipBatchResponseHandler(), thread_pool) # GOSSIP_BATCH_RESPONSE 2) Verifies signature dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, signature_verifier.GossipBatchResponseSignatureVerifier(), sig_pool) # GOSSIP_BATCH_RESPONSE 3) Check batch structure dispatcher.add_handler( validator_pb2.Message.GOSSIP_BATCH_RESPONSE, structure_verifier.GossipBatchResponseStructureVerifier(), thread_pool) # GOSSIP_BATCH_RESPONSE 4) Send message to completer dispatcher.add_handler(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, CompleterGossipBatchResponseHandler(completer), thread_pool) dispatcher.add_handler(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, ResponderBatchResponseHandler(responder, gossip), thread_pool)