def __get_eservice_client__(state, save_file, eservice_url) : try : contract = get_contract(state, save_file) except Exception as e : raise Exception('unable to load the contract') if eservice_url not in ['random', 'preferred'] : try : eservice_client = EnclaveServiceClient(eservice_url) except Exception as e : raise Exception('unable to connect to enclave service; {0}'.format(str(e))) if eservice_client.enclave_id not in contract.provisioned_enclaves : raise Exception('requested enclave not provisioned for the contract; %s', eservice_url) else : if eservice_url == 'preferred' : enclave_id = contract.extra_data.get('preferred-enclave', random.choice(contract.provisioned_enclaves)) else : enclave_id = random.choice(contract.provisioned_enclaves) eservice_info = eservice_db.get_by_enclave_id(enclave_id) if eservice_info is None : raise Exception('attempt to use an unknown enclave; %s', enclave_id) try : eservice_client = EnclaveServiceClient(eservice_info.url) except Exception as e : raise Exception('unable to connect to enclave service; {0}'.format(str(e))) return eservice_client
def __init__(self, operation, request_originator_keys, contract, **kwargs): if not self.__ops__[operation]: raise ValueError('invalid operation') self.operation = operation self.contract_id = contract.contract_id self.creator_id = contract.creator_id self.enclave_service = kwargs.get('enclave_service') if self.enclave_service == 'random': enclave_id = random.choice(list(contract.enclave_map.keys())) try: #use the eservice database to get the client einfo = eservice_db.get_by_enclave_id(enclave_id) self.enclave_service = einfo.client except Exception as e: raise Exception( 'failed to get enclave client using database: %s', str(e)) self.encrypted_state_encryption_key = contract.get_state_encryption_key( self.enclave_service.enclave_id) self.originator_keys = request_originator_keys self.make_channel_keys() self.session_key = crypto.SKENC_GenerateKey() self.contract_code = contract.contract_code self.contract_state = contract.contract_state self.message = ContractMessage(self.originator_keys, self.channel_id, **kwargs) self.replication_params = contract.replication_params self.request_number = ContractRequest.__request_number__ ContractRequest.__request_number__ += 1
def __init__(self, request_originator_keys, contract, **kwargs) : self.contract_id = contract.contract_id self.creator_id = contract.creator_id self.enclave_service = kwargs.get('enclave_service') if self.enclave_service == 'random': enclave_id = random.choice(list(contract.enclave_map.keys())) try: #use the eservice database to get the client einfo = eservice_db.get_by_enclave_id(enclave_id) self.enclave_service = einfo.client except Exception as e: raise Exception('failed to get enclave client using database: %s', str(e)) self.encrypted_state_encryption_key = contract.get_state_encryption_key(self.enclave_service.enclave_id) self.originator_keys = request_originator_keys self.make_channel_keys() self.session_key = crypto.SKENC_GenerateKey() self.replication_params = contract.replication_params self.request_number = ContractRequest.get_request_number()
# ----------------------------------------------------------------- # logger.info('create and load the database from the provided URLs') # ----------------------------------------------------------------- names = [] enclave_count = 0 for url in options.url: names.append('enclave_{0}'.format(enclave_count)) eservice_db.add_by_url(ledger_config, url, name=names[enclave_count]) enclave_count += 1 # ----------------------------------------------------------------- # logger.info('verify that the information in the database is consistent') # ----------------------------------------------------------------- for e in names: einfo_by_name = eservice_db.get_by_name(e) einfo_by_enclave_id = eservice_db.get_by_enclave_id( einfo_by_name.client.enclave_id) # this can probably be simplified to just a comparison of # the two objects assert einfo_by_name.name == e assert einfo_by_enclave_id.name == e assert einfo_by_name.name == einfo_by_enclave_id.name assert einfo_by_name.enclave_id == einfo_by_enclave_id.enclave_id # ----------------------------------------------------------------- # logger.info('verify that database can be saved and loaded') # ----------------------------------------------------------------- initial_hash = compute_database_hash() eservice_db.save_database(options.eservice_db, overwrite=True) #load db freshly and check
def send_to_contract(state, save_file, message, eservice_url=None, quiet=False, wait=False, commit=True): # ---------- load the invoker's keys ---------- try: keyfile = state.get(['Key', 'FileName']) keypath = state.get(['Key', 'SearchPath']) client_keys = ServiceKeys.read_from_file(keyfile, keypath) except Exception as e: raise Exception('unable to load client keys; {0}'.format(str(e))) # ---------- read the contract ---------- try: contract = get_contract(state, save_file) except Exception as e: raise Exception('unable to load the contract') # ---------- set up the enclave service ---------- if eservice_url is None: eservice_url = 'preferred' if eservice_url not in ['random', 'preferred']: try: eservice_client = EnclaveServiceClient(eservice_url) except Exception as e: raise Exception('unable to connect to enclave service; {0}'.format( str(e))) if eservice_client.enclave_id not in contract.provisioned_enclaves: raise Exception( 'requested enclave not provisioned for the contract; %s', eservice_url) else: if eservice_url == 'preferred': enclave_id = contract.extra_data.get( 'preferred-enclave', random.choice(contract.provisioned_enclaves)) else: enclave_id = random.choice(contract.provisioned_enclaves) eservice_info = eservice_db.get_by_enclave_id(enclave_id) if eservice_info is None: raise Exception('attempt to use an unknown enclave; %s', enclave_id) try: eservice_client = EnclaveServiceClient(eservice_info.url) except Exception as e: raise Exception('unable to connect to enclave service; {0}'.format( str(e))) # ---------- send the message to the enclave service ---------- try: update_request = contract.create_update_request( client_keys, message, eservice_client) update_response = update_request.evaluate() except Exception as e: raise Exception('enclave failed to evaluate expression; {0}'.format( str(e))) if not update_response.status: # not sure if this should throw an exception which would # terminate the script or if it should just return an # empty string that can be tested for later # if not quiet : # print("FAILED: {0}".format(update_response.invocation_response)) # return '' raise ValueError(update_response.invocation_response) if not quiet: print(update_response.invocation_response) data_directory = state.get(['Contract', 'DataDirectory']) ledger_config = state.get(['Ledger']) if update_response.state_changed and commit: contract.set_state(update_response.raw_state) # asynchronously submit the commit task: (a commit task replicates # change-set and submits the corresponding transaction) try: update_response.commit_asynchronously(ledger_config) except Exception as e: raise Exception('failed to submit commit: %s', str(e)) # wait for the commit to finish. # TDB: # 1. make wait_for_commit a separate shell command. # 2. Add a provision to specify commit dependencies as input to send command. # 3. Return commit_id after send command back to shell so as to use as input # commit_dependency in a future send command try: txn_id = update_response.wait_for_commit() if txn_id is None: raise Exception( "Did not receive txn id for the send operation") except Exception as e: raise Exception("Error while waiting for commit: %s", str(e)) try: contract.contract_state.save_to_cache(data_dir=data_directory) except Exception as e: logger.exception('failed to save the new state in the cache') return update_response.invocation_response
def __replication_worker__(service_id, pending_tasks_queue, condition_variable_for_setup): """ Worker thread that replicates to a specific storage service""" # set up the service client try: einfo = service_db.get_by_enclave_id(service_id) service_client = einfo.client init_sucess = True except: logger.info("Failed to set up service client for service id %s", str(service_id)) # mark the service as unusable __services_to_ignore__.add(service_id) #exit the thread init_sucess = False # notify the manager that init was attempted condition_variable_for_setup.acquire() condition_variable_for_setup.notify() condition_variable_for_setup.release() # exit the thread if init failed if not init_sucess: return try: while True: # wait for a new task, task is the response object try: response = pending_tasks_queue.get(timeout=1.0) except: # check for termination signal if __stop_service__: logger.info( "Exiting Replication worker thread for service at %s", str(service_client.ServiceURL)) break else: continue #check if the task is already complete. If so go to the next one replication_request = response.replication_request if replication_request.is_completed: continue # replicate now! block_data_list = ContractState.block_data_generator(replication_request.contract_id, \ replication_request.blocks_to_replicate, replication_request.data_dir) expiration = replication_request.availability_duration request_id = response.commit_id[2] try: fail_task = False response_from_replication = service_client.store_blocks( block_data_list, expiration) if response_from_replication is None: fail_task = True logger.info( "No response from storage service %s for replication request %d", str(service_client.ServiceURL), request_id) except Exception as e: fail_task = True logger.info( "Replication request %d got an exception from %s: %s", request_id, str(service_client.ServiceURL), str(e)) # update the set of services where replication is completed replication_request.update_set_of_services_where_replicated( service_id, fail_task) # check if the overall task can be marked as successful or failed: if len(replication_request.successful_services ) >= replication_request.num_provable_replicas: replication_request.mark_as_completed( response.call_back_after_replication) elif len(replication_request.unsuccessful_services) > len( replication_request.service_ids ) - replication_request.num_provable_replicas: replication_request.mark_as_failed() # Finally, if the task failed, mark the service as unreliable # (this may be a bit harsh, we will refine this later based on the nature of the failure) if fail_task: __services_to_ignore__.add(service_id) logger.info( "Ignoring service at %s for rest of replication attempts", str(service_client.ServiceURL)) #exit the thread break except Exception as e: logger.info("Replication Worker exception %s", str(e))