github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/sawtooth_validator/journal/responder.py (about) 1 # Copyright 2017 Intel Corporation 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # ------------------------------------------------------------------------------ 15 16 import logging 17 from threading import RLock 18 19 from sawtooth_validator.networking.dispatch import Handler 20 from sawtooth_validator.networking.dispatch import HandlerResult 21 from sawtooth_validator.networking.dispatch import HandlerStatus 22 from sawtooth_validator.journal.timed_cache import TimedCache 23 from sawtooth_validator.protobuf import network_pb2 24 from sawtooth_validator.protobuf import validator_pb2 25 26 LOGGER = logging.getLogger(__name__) 27 28 CACHE_KEEP_TIME = 300 29 30 31 class Responder(object): 32 def __init__(self, 33 completer, 34 cache_keep_time=300, 35 cache_purge_frequency=30): 36 self.completer = completer 37 self.pending_requests = TimedCache(cache_keep_time, 38 cache_purge_frequency) 39 self._lock = RLock() 40 41 def check_for_block(self, block_id): 42 # Ask Completer 43 if block_id == "HEAD": 44 block = self.completer.get_chain_head() 45 else: 46 block = self.completer.get_block(block_id) 47 return block 48 49 def check_for_batch(self, batch_id): 50 batch = self.completer.get_batch(batch_id) 51 return batch 52 53 def check_for_batch_by_transaction(self, transaction_id): 54 batch = self.completer.get_batch_by_transaction(transaction_id) 55 return batch 56 57 def already_requested(self, requested_id): 58 with self._lock: 59 if requested_id in self.pending_requests: 60 return True 61 return False 62 63 def add_request(self, requested_id, connection_id): 64 with self._lock: 65 if requested_id in self.pending_requests: 66 if connection_id not in self.pending_requests[requested_id]: 67 self.pending_requests[requested_id] += [connection_id] 68 69 else: 70 self.pending_requests[requested_id] = [connection_id] 71 72 def get_request(self, requested_id): 73 with self._lock: 74 return self.pending_requests.get(requested_id) 75 76 def remove_request(self, requested_id): 77 with self._lock: 78 if requested_id in self.pending_requests: 79 del self.pending_requests[requested_id] 80 81 82 class BlockResponderHandler(Handler): 83 def __init__(self, responder, gossip): 84 self._responder = responder 85 self._gossip = gossip 86 self._seen_requests = TimedCache(CACHE_KEEP_TIME) 87 88 def handle(self, connection_id, message_content): 89 block_request_message = network_pb2.GossipBlockRequest() 90 block_request_message.ParseFromString(message_content) 91 if block_request_message.nonce in self._seen_requests: 92 LOGGER.debug("Received repeat GossipBlockRequest from %s", 93 connection_id) 94 95 return HandlerResult(HandlerStatus.DROP) 96 97 block_id = block_request_message.block_id 98 block = self._responder.check_for_block(block_id) 99 if block is None: 100 # No block found, broadcast original message to other peers 101 # and add to pending requests 102 if block_id == "HEAD": 103 LOGGER.debug("No chain head available. Cannot respond to block" 104 " requests.") 105 else: 106 if not self._responder.already_requested(block_id): 107 if block_request_message.time_to_live > 0: 108 time_to_live = block_request_message.time_to_live 109 block_request_message.time_to_live = time_to_live - 1 110 self._gossip.broadcast( 111 block_request_message, 112 validator_pb2.Message.GOSSIP_BLOCK_REQUEST, 113 exclude=[connection_id]) 114 115 self._seen_requests[block_request_message.nonce] = \ 116 block_request_message.block_id 117 118 self._responder.add_request(block_id, connection_id) 119 else: 120 LOGGER.debug("Block %s has already been requested", 121 block_id) 122 123 self._responder.add_request(block_id, connection_id) 124 else: 125 LOGGER.debug("Responding to block requests: %s", 126 block.get_block().header_signature) 127 128 block_response = network_pb2.GossipBlockResponse( 129 content=block.get_block().SerializeToString()) 130 131 self._gossip.send(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, 132 block_response.SerializeToString(), 133 connection_id) 134 135 return HandlerResult(HandlerStatus.PASS) 136 137 138 class ResponderBlockResponseHandler(Handler): 139 def __init__(self, responder, gossip): 140 self._responder = responder 141 self._gossip = gossip 142 143 def handle(self, connection_id, message_content): 144 block, message_content = message_content 145 146 open_request = self._responder.get_request(block.header_signature) 147 148 if open_request is None: 149 return HandlerResult(status=HandlerStatus.PASS) 150 151 for connection in open_request: 152 LOGGER.debug("Responding to block request: Send %s to %s", 153 block.header_signature, 154 connection) 155 try: 156 self._gossip.send(validator_pb2.Message.GOSSIP_BLOCK_RESPONSE, 157 message_content, 158 connection) 159 except ValueError: 160 LOGGER.debug("Can't send block response %s to closed " 161 "connection %s", 162 block.header_signature, 163 connection) 164 165 self._responder.remove_request(block.header_signature) 166 167 return HandlerResult(HandlerStatus.PASS) 168 169 170 class BatchByBatchIdResponderHandler(Handler): 171 def __init__(self, responder, gossip): 172 self._responder = responder 173 self._gossip = gossip 174 self._seen_requests = TimedCache(CACHE_KEEP_TIME) 175 176 def handle(self, connection_id, message_content): 177 batch_request_message = network_pb2.GossipBatchByBatchIdRequest() 178 batch_request_message.ParseFromString(message_content) 179 if batch_request_message.nonce in self._seen_requests: 180 LOGGER.debug("Received repeat GossipBatchByBatchIdRequest from %s", 181 connection_id) 182 return HandlerResult(HandlerStatus.DROP) 183 184 batch = None 185 batch = self._responder.check_for_batch(batch_request_message.id) 186 187 if batch is None: 188 # No batch found, broadcast original message to other peers 189 # and add to pending requests 190 if not self._responder.already_requested(batch_request_message.id): 191 192 if batch_request_message.time_to_live > 0: 193 time_to_live = batch_request_message.time_to_live 194 batch_request_message.time_to_live = time_to_live - 1 195 self._gossip.broadcast( 196 batch_request_message, 197 validator_pb2.Message.GOSSIP_BATCH_BY_BATCH_ID_REQUEST, 198 exclude=[connection_id]) 199 200 self._seen_requests[batch_request_message.nonce] = \ 201 batch_request_message.id 202 203 self._responder.add_request(batch_request_message.id, 204 connection_id) 205 else: 206 LOGGER.debug("Batch %s has already been requested", 207 batch_request_message.id) 208 209 self._responder.add_request(batch_request_message.id, 210 connection_id) 211 else: 212 LOGGER.debug("Responding to batch requests %s", 213 batch.header_signature) 214 215 batch_response = network_pb2.GossipBatchResponse( 216 content=batch.SerializeToString(), 217 ) 218 219 self._gossip.send(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, 220 batch_response.SerializeToString(), 221 connection_id) 222 223 return HandlerResult(HandlerStatus.PASS) 224 225 226 class BatchByTransactionIdResponderHandler(Handler): 227 def __init__(self, responder, gossip): 228 self._responder = responder 229 self._gossip = gossip 230 self._seen_requests = TimedCache(CACHE_KEEP_TIME) 231 232 def handle(self, connection_id, message_content): 233 batch_request_message = network_pb2.GossipBatchByTransactionIdRequest() 234 batch_request_message.ParseFromString(message_content) 235 if batch_request_message.nonce in self._seen_requests: 236 LOGGER.debug("Received repeat GossipBatchByTransactionIdRequest" 237 " from %s", connection_id) 238 239 return HandlerResult(HandlerStatus.DROP) 240 241 batch = None 242 batches = [] 243 unfound_txn_ids = [] 244 not_requested = [] 245 for txn_id in batch_request_message.ids: 246 batch = self._responder.check_for_batch_by_transaction( 247 txn_id) 248 249 # The txn_id was not found. 250 if batch is None: 251 unfound_txn_ids.append(txn_id) 252 if not self._responder.already_requested(txn_id): 253 not_requested.append(txn_id) 254 else: 255 LOGGER.debug("Batch containing Transaction %s has already " 256 "been requested", txn_id) 257 258 # Check to see if a previous txn was in the same batch. 259 elif batch not in batches: 260 batches.append(batch) 261 262 batch = None 263 264 if batches == [] and len(not_requested) == \ 265 len(batch_request_message.ids): 266 267 if batch_request_message.time_to_live > 0: 268 time_to_live = batch_request_message.time_to_live 269 batch_request_message.time_to_live = time_to_live - 1 270 self._gossip.broadcast( 271 batch_request_message, 272 validator_pb2.Message. 273 GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, 274 exclude=[connection_id]) 275 276 self._seen_requests[batch_request_message.nonce] = \ 277 batch_request_message.ids 278 279 for txn_id in batch_request_message.ids: 280 self._responder.add_request(txn_id, connection_id) 281 282 elif unfound_txn_ids != []: 283 if not_requested != []: 284 if batch_request_message.time_to_live > 0: 285 self._seen_requests[batch_request_message.nonce] = \ 286 batch_request_message.ids 287 new_request = \ 288 network_pb2.GossipBatchByTransactionIdRequest() 289 # only request batches we have not requested already 290 new_request.ids.extend(not_requested) 291 # Keep same nonce as original message 292 new_request.nonce = batch_request_message.nonce 293 time_to_live = batch_request_message.time_to_live 294 new_request.time_to_live = time_to_live - 1 295 296 self._gossip.broadcast( 297 new_request, 298 validator_pb2.Message. 299 GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST, 300 exclude=[connection_id]) 301 # Add all requests to responder 302 for txn_id in unfound_txn_ids: 303 self._responder.add_request(txn_id, connection_id) 304 else: 305 # Add all requests to responder 306 for txn_id in unfound_txn_ids: 307 self._responder.add_request(txn_id, connection_id) 308 309 if batches != []: 310 for batch in batches: 311 LOGGER.debug("Responding to batch requests %s", 312 batch.header_signature) 313 314 batch_response = network_pb2.GossipBatchResponse( 315 content=batch.SerializeToString(), 316 ) 317 318 self._gossip.send(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, 319 batch_response.SerializeToString(), 320 connection_id) 321 322 return HandlerResult(HandlerStatus.PASS) 323 324 325 class ResponderBatchResponseHandler(Handler): 326 def __init__(self, responder, gossip): 327 self._responder = responder 328 self._gossip = gossip 329 330 def handle(self, connection_id, message_content): 331 batch, message_content = message_content 332 333 open_request = self._responder.get_request(batch.header_signature) 334 335 if open_request is None: 336 open_request = [] 337 338 requests_to_remove = [batch.header_signature] 339 for txn in batch.transactions: 340 requests_by_txn = self._responder.get_request(txn.header_signature) 341 if requests_by_txn is not None: 342 open_request += requests_by_txn 343 requests_to_remove += [txn.header_signature] 344 345 for connection in open_request: 346 LOGGER.debug("Responding to batch requests: Send %s to %s", 347 batch.header_signature, 348 connection) 349 try: 350 self._gossip.send(validator_pb2.Message.GOSSIP_BATCH_RESPONSE, 351 message_content, 352 connection) 353 except ValueError: 354 LOGGER.debug("Can't send batch response %s to closed " 355 "connection %s", 356 batch.header_signature, 357 connection) 358 359 for requested_id in requests_to_remove: 360 self._responder.remove_request(requested_id) 361 362 return HandlerResult(HandlerStatus.PASS)