def read_from_file(cls, ledger_config, basename, data_dir='./data'): filename = putils.build_file_name(basename, data_dir, '.pdo') logger.debug('load contract information from %s', filename) if os.path.exists(filename) is not True: raise FileNotFoundError(errno.ENOENT, "contract data file does not exist", filename) try: with open(filename, "r") as contract_file: contract_info = json.load(contract_file) except Exception as e: logger.warn('load contract information file failed; %s', str(e)) raise Exception("invalid contract file; {}".format(filename)) try: code_info = contract_info['contract_code'] code = ContractCode(code_info['Code'], code_info['Name'], code_info['Nonce']) except KeyError as ke: logger.error('invalid contract data file; missing %s', str(ke)) raise Exception("invalid contract file; {}".format(filename)) except Exception as e: logger.error('error occurred retreiving contract code; %s', str(e)) raise Exception("invalid contract file; {}".format(filename)) ## need to handle the case where the contract has been registered ## but the initial state has not been committed try: contract_id = contract_info['contract_id'] current_state_hash = ContractState.get_current_state_hash( ledger_config, contract_id) except Exception as e: logger.error('error occurred retreiving contract state hash; %s', str(e)) raise Exception('invalid contract file; {}'.format(filename)) try: state = ContractState.read_from_cache(contract_id, current_state_hash, data_dir=data_dir) if state is None: state = ContractState.get_from_ledger(ledger_config, contract_id, current_state_hash) state.save_to_cache(data_dir=data_dir) except Exception as e: logger.error('error occurred retreiving contract state; %s', str(e)) raise Exception("invalid contract file; {}".format(filename)) obj = cls(code, state, contract_info['contract_id'], contract_info['creator_id']) for enclave in contract_info['enclaves_info']: obj.set_state_encryption_key( enclave['contract_enclave_id'], enclave['encrypted_contract_state_encryption_key']) return obj
def __init__(self, request, response, **kwargs): super().__init__(request, response, **kwargs) self.state_changed = True self.request_number = request.request_number self.operation = 'initialize' self.metadata_hash = crypto.base64_to_byte_array( response['MetadataHash']) self.signature = response['Signature'] # save the information we will need for the transaction state_hash_b64 = response['StateHash'] self.new_state_hash = crypto.base64_to_byte_array(state_hash_b64) message = self.serialize_for_signing() if not self.verify_enclave_signature(message, request.enclave_keys): raise InvocationException('failed to verify enclave signature') self.raw_state = self.enclave_service.get_block(state_hash_b64) self.new_state_object = ContractState(self.contract_id, self.raw_state) self.new_state_object.pull_state_from_eservice(self.enclave_service) # compute ids of blocks in the change set (used for replication) self.new_state_object.compute_ids_of_newblocks( request.contract_state.component_block_ids) self.replication_params = request.replication_params
def __init__(self, request, response) : """ Initialize a contract response object :param request: the ContractRequest object corresponding to the response :param response: diction containing the response from the enclave """ self.status = response['Status'] self.invocation_response_raw = response['InvocationResponse'] self.invocation_response = invocation_response(response['InvocationResponse']) self.state_changed = response['StateChanged'] self.new_state_object = request.contract_state #if the new state is same as the old state, then change set is empty self.new_state_object.changed_block_ids=[] self.request_number = request.request_number self.operation = request.operation if self.status and self.state_changed : self.signature = response['Signature'] state_hash_b64 = response['StateHash'] # we have another mismatch between the field names in the enclave # and the field names expected in the transaction; this needs to # be fixed at some point self.dependencies = [] for dependency in response['Dependencies'] : contract_id = dependency['ContractID'] state_hash = dependency['StateHash'] self.dependencies.append({'contract_id' : contract_id, 'state_hash' : state_hash}) # save the information we will need for the transaction self.channel_keys = request.channel_keys self.channel_id = request.channel_id self.contract_id = request.contract_id self.creator_id = request.creator_id self.code_hash = request.contract_code.compute_hash() self.message_hash = request.message.compute_hash() self.new_state_hash = crypto.base64_to_byte_array(state_hash_b64) self.originator_keys = request.originator_keys self.enclave_service = request.enclave_service self.old_state_hash = () if request.operation != 'initialize' : self.old_state_hash = ContractState.compute_hash(request.contract_state.raw_state) if not self.__verify_enclave_signature(request.enclave_keys) : raise Exception('failed to verify enclave signature') self.raw_state = self.enclave_service.get_block(state_hash_b64) self.new_state_object = ContractState(self.contract_id, self.raw_state) self.new_state_object.pull_state_from_eservice(self.enclave_service) # compute ids of blocks in the change set (used for replication) self.new_state_object.compute_ids_of_newblocks(request.contract_state.component_block_ids) self.replication_params = request.replication_params
class UpdateStateResponse(ContractResponse): def __init__(self, request, response, **kwargs): super().__init__(request, response, **kwargs) self.state_changed = True self.request_number = request.request_number self.operation = 'update' self.signature = response['Signature'] # we have another mismatch between the field names in the enclave # and the field names expected in the transaction; this needs to # be fixed at some point for dependency in response['Dependencies']: contract_id = dependency['ContractID'] state_hash = dependency['StateHash'] self.dependencies.append({ 'contract_id': contract_id, 'state_hash': state_hash }) # save the information we will need for the transaction state_hash_b64 = response['StateHash'] self.new_state_hash = crypto.base64_to_byte_array(state_hash_b64) self.old_state_hash = ContractState.compute_hash( request.contract_state.raw_state) message = self.serialize_for_signing() if not self.verify_enclave_signature(message, request.enclave_keys): raise Exception('failed to verify enclave signature') self.raw_state = self.enclave_service.get_block(state_hash_b64) self.new_state_object = ContractState(self.contract_id, self.raw_state) self.new_state_object.pull_state_from_eservice(self.enclave_service) # compute ids of blocks in the change set (used for replication) self.new_state_object.compute_ids_of_newblocks( request.contract_state.component_block_ids) self.replication_params = request.replication_params # ------------------------------------------------------- def serialize_for_signing(self): """serialize the response for enclave signature verification""" message = super().serialize_for_signing() message += self.old_state_hash message += self.new_state_hash for dependency in self.dependencies: message += crypto.string_to_byte_array(dependency['contract_id']) message += crypto.string_to_byte_array(dependency['state_hash']) return message
def __init__(self, request, response) : """ Initialize a contract response object :param request: the ContractRequest object corresponding to the response :param response: diction containing the response from the enclave """ self.status = response['Status'] self.result = response['Result'] self.state_changed = response['StateChanged'] if self.status and self.state_changed : self.signature = response['Signature'] state_hash_b64 = response['StateHash'] # we have another mismatch between the field names in the enclave # and the field names expected in the transaction; this needs to # be fixed at some point self.dependencies = [] for dependency in response['Dependencies'] : contract_id = dependency['ContractID'] state_hash = dependency['StateHash'] self.dependencies.append({'contract_id' : contract_id, 'state_hash' : state_hash}) # save the information we will need for the transaction self.channel_keys = request.channel_keys self.contract_id = request.contract_id self.creator_id = request.creator_id self.code_hash = request.contract_code.compute_hash() self.message_hash = request.message.compute_hash() self.new_state_hash = crypto.base64_to_byte_array(state_hash_b64) self.originator_keys = request.originator_keys self.enclave_service = request.enclave_service self.old_state_hash = () if request.operation != 'initialize' : self.old_state_hash = ContractState.compute_hash(request.contract_state.encrypted_state) if not self.__verify_enclave_signature(request.enclave_keys) : raise Exception('failed to verify enclave signature') # Retrieve the encrypted state from the enclave's block store # Note that this channel is untrusted - must verify the retrieved data has the correct hash! # This is intentionally done after the signature verification encrypted_state_u_b64 = self.enclave_service.block_store_get(state_hash_b64) encrypted_state_u_hash_b64 = ContractState.compute_hash(encrypted_state_u_b64, encoding='b64') if (state_hash_b64 != encrypted_state_u_hash_b64): raise Exception('Encrypted state from block store has incorrect hash!') self.encrypted_state = encrypted_state_u_b64;
def __init__(self, request, response): """ Initialize a contract response object :param request: the ContractRequest object corresponding to the response :param response: diction containing the response from the enclave """ self.status = response['Status'] self.result = response['Result'] self.state_changed = response['StateChanged'] if self.status and self.state_changed: self.signature = response['Signature'] self.encrypted_state = response['State'] # we have another mismatch between the field names in the enclave # and the field names expected in the transaction; this needs to # be fixed at some point self.dependencies = [] for dependency in response['Dependencies']: contract_id = dependency['ContractID'] state_hash = dependency['StateHash'] self.dependencies.append({ 'contract_id': contract_id, 'state_hash': state_hash }) # save the information we will need for the transaction self.channel_keys = request.channel_keys self.contract_id = request.contract_id self.creator_id = request.creator_id self.code_hash = request.contract_code.compute_hash() self.message_hash = request.message.compute_hash() self.new_state_hash = ContractState.compute_hash( self.encrypted_state) self.originator_keys = request.originator_keys self.enclave_service = request.enclave_service self.old_state_hash = () if request.operation != 'initialize': self.old_state_hash = ContractState.compute_hash( request.contract_state.encrypted_state) if not self.__verify_enclave_signature(request.enclave_keys): raise Exception('failed to verify enclave signature')
def __init__(self, operation, request_originator_keys, contract, **kwargs) : super().__init__(request_originator_keys, contract, **kwargs) if not self.__ops__[operation] : raise ValueError('invalid operation') self.operation = operation self.contract_code = contract.contract_code self.contract_state = ContractState.create_new_state(self.contract_id) self.message = ContractMessage(self.originator_keys, self.channel_id, **kwargs)
def import_contract(cls, config, contract_file, contract_name): """create a new contract pdo object and save it """ try: contract_info = json.load(contract_file) except: logger.warn('failed to retrieve contract information') return None try: code_info = contract_info['contract_code'] code = pdo_contract_code(code_info['Code'], code_info['Name'], code_info['Nonce']) except KeyError as ke: logger.error('invalid contract data file; missing %s', str(ke)) raise Exception("invalid contract file; {}".format()) except Exception as e: logger.error('error occurred retreiving contract code; %s', str(e)) raise Exception("invalid contract file; {}".format(contract_name)) ## need to handle the case where the contract has been registered ## but the initial state has not been committed ledger_config = config.get('Sawtooth', {}) try: contract_id = contract_info['contract_id'] current_state_hash = pdo_contract_state.get_current_state_hash( ledger_config, contract_id) except Exception as e: logger.error('error occurred retreiving contract state hash; %s', str(e)) raise Exception('invalid contract file; {}'.format(contract_name)) try: path_config = config.get('ContentPaths') state_root = os.path.realpath( path_config.get('State', os.path.join(os.environ['HOME'], '.toxaway'))) state = pdo_contract_state.read_from_cache(contract_id, current_state_hash, data_dir=state_root) if state is None: state = pdo_contract_state.get_from_ledger( ledger_config, contract_id, current_state_hash) state.save_to_cache(data_dir=data_dir) except Exception as e: logger.error('error occurred retreiving contract state; %s', str(e)) raise Exception("invalid contract file; {}".format(contract_name)) extra_data = contract_info.get('extra_data', {}) extra_data['name'] = contract_name extra_data['update-enclave'] = 'random' extra_data['invoke-enclave'] = 'random' obj = cls(code, state, contract_info['contract_id'], contract_info['creator_id'], extra_data=extra_data) for enclave in contract_info['enclaves_info']: obj.set_state_encryption_key( enclave['contract_enclave_id'], enclave['encrypted_contract_state_encryption_key']) obj.save(config) return obj
class ContractResponse(object) : """ Class for managing the contract operation response from an enclave service """ # ------------------------------------------------------- def __init__(self, request, response) : """ Initialize a contract response object :param request: the ContractRequest object corresponding to the response :param response: diction containing the response from the enclave """ self.status = response['Status'] self.result = response['Result'] self.state_changed = response['StateChanged'] self.new_state_object = request.contract_state if self.status and self.state_changed : self.signature = response['Signature'] state_hash_b64 = response['StateHash'] # we have another mismatch between the field names in the enclave # and the field names expected in the transaction; this needs to # be fixed at some point self.dependencies = [] for dependency in response['Dependencies'] : contract_id = dependency['ContractID'] state_hash = dependency['StateHash'] self.dependencies.append({'contract_id' : contract_id, 'state_hash' : state_hash}) # save the information we will need for the transaction self.channel_keys = request.channel_keys self.contract_id = request.contract_id self.creator_id = request.creator_id self.code_hash = request.contract_code.compute_hash() self.message_hash = request.message.compute_hash() self.new_state_hash = crypto.base64_to_byte_array(state_hash_b64) self.originator_keys = request.originator_keys self.enclave_service = request.enclave_service self.old_state_hash = () if request.operation != 'initialize' : self.old_state_hash = ContractState.compute_hash(request.contract_state.encrypted_state) if not self.__verify_enclave_signature(request.enclave_keys) : raise Exception('failed to verify enclave signature') self.encrypted_state = self.enclave_service.block_store_get(state_hash_b64) self.new_state_object = ContractState(self.contract_id, self.encrypted_state) self.new_state_object.pull_state_from_eservice(self.enclave_service) # ------------------------------------------------------- def __verify_enclave_signature(self, enclave_keys) : """verify the signature of the response """ message = self.__serialize_for_signing() return enclave_keys.verify(message, self.signature, encoding = 'b64') # ------------------------------------------------------- def __serialize_for_signing(self) : """serialize the response for enclave signature verification""" message = crypto.string_to_byte_array(self.channel_keys.txn_public) message += crypto.string_to_byte_array(self.contract_id) message += crypto.string_to_byte_array(self.creator_id) message += self.code_hash message += self.message_hash message += self.new_state_hash message += self.old_state_hash for dependency in self.dependencies : message += crypto.string_to_byte_array(dependency['contract_id']) message += crypto.string_to_byte_array(dependency['state_hash']) return message # ------------------------------------------------------- def submit_initialize_transaction(self, ledger_config, **extra_params) : """submit the initialize transaction to the ledger """ if self.status is False : raise Exception('attempt to submit failed initialization transactions') global transaction_dependencies # an initialize operation has no previous state assert not self.old_state_hash initialize_submitter = Submitter( ledger_config['LedgerURL'], key_str = self.channel_keys.txn_private) b64_message_hash = crypto.byte_array_to_base64(self.message_hash) b64_new_state_hash = crypto.byte_array_to_base64(self.new_state_hash) b64_code_hash = crypto.byte_array_to_base64(self.code_hash) txnid = initialize_submitter.submit_ccl_initialize_from_data( self.originator_keys.signing_key, self.originator_keys.verifying_key, self.channel_keys.txn_public, self.enclave_service.enclave_id, self.signature, self.contract_id, b64_message_hash, b64_new_state_hash, self.encrypted_state, b64_code_hash, **extra_params) if txnid : transaction_dependencies.SaveDependency(self.contract_id, b64_new_state_hash, txnid) return txnid # ------------------------------------------------------- def submit_update_transaction(self, ledger_config, **extra_params): """submit the update transaction to the ledger """ if self.status is False : raise Exception('attempt to submit failed update transaction') global transaction_dependencies # there must be a previous state hash if this is # an update assert self.old_state_hash update_submitter = Submitter( ledger_config['LedgerURL'], key_str = self.channel_keys.txn_private) b64_message_hash = crypto.byte_array_to_base64(self.message_hash) b64_new_state_hash = crypto.byte_array_to_base64(self.new_state_hash) b64_old_state_hash = crypto.byte_array_to_base64(self.old_state_hash) # convert contract dependencies into transaction dependencies # to ensure that the sawtooth validator does not attempt to # re-order the transactions since it is unaware of the semantics # of the contract dependencies txn_dependencies = set() if extra_params.get('transaction_dependency_list') : txn_dependencies.update(extra_params['transaction_dependency_list']) txnid = transaction_dependencies.FindDependency(ledger_config, self.contract_id, b64_old_state_hash) if txnid : txn_dependencies.add(txnid) for dependency in self.dependencies : contract_id = dependency['contract_id'] state_hash = dependency['state_hash'] txnid = transaction_dependencies.FindDependency(ledger_config, contract_id, state_hash) if txnid : txn_dependencies.add(txnid) else : raise Exception('failed to find dependency; {0}:{1}'.format(contract_id, state_hash)) if txn_dependencies : extra_params['transaction_dependency_list'] = list(txn_dependencies) # now send off the transaction to the ledger txnid = update_submitter.submit_ccl_update_from_data( self.originator_keys.verifying_key, self.channel_keys.txn_public, self.enclave_service.enclave_id, self.signature, self.contract_id, b64_message_hash, b64_new_state_hash, b64_old_state_hash, self.encrypted_state, self.dependencies, **extra_params) if txnid : transaction_dependencies.SaveDependency(self.contract_id, b64_new_state_hash, txnid) return txnid
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))
def read_from_file(cls, ledger_config, basename, data_dir=None): filename = putils.build_file_name(basename, data_dir, cls.__path__, cls.__extension__) logger.debug('load contract information from %s', filename) if os.path.exists(filename) is not True: raise FileNotFoundError(errno.ENOENT, "contract data file does not exist", filename) try: with open(filename, "r") as contract_file: contract_info = json.load(contract_file) except Exception as e: logger.warn('load contract information file failed; %s', str(e)) raise Exception("invalid contract file; {}".format(filename)) try: code_info = contract_info['contract_code'] comp_report = None if not code_info['CompilationReport'].get( 'CompilerVerifyingKey') is None: comp_report = ContractCompilationReport.init_from_dict( code_info['CompilationReport']) code = ContractCode(code_info['Code'], code_info['Name'], code_info['Nonce'], compilation_report=comp_report) except KeyError as ke: logger.error('invalid contract data file; missing %s', str(ke)) raise Exception("invalid contract file; {}".format(filename)) except Exception as e: logger.error('error occurred retreiving contract code; %s', str(e)) raise Exception("invalid contract file; {}".format(filename)) ## need to handle the case where the contract has been registered ## but the initial state has not been committed try: contract_id = contract_info['contract_id'] current_state_hash = ContractState.get_current_state_hash( ledger_config, contract_id) except Exception as e: logger.error('error occurred retreiving contract state hash; %s', str(e)) raise Exception('invalid contract file; {}'.format(filename)) try: state = ContractState.read_from_cache(contract_id, current_state_hash, data_dir=data_dir) if state is None: state = ContractState.get_from_ledger(ledger_config, contract_id, current_state_hash) state.save_to_cache(data_dir=data_dir) except Exception as e: logger.error('error occurred retreiving contract state; %s', str(e)) raise Exception("invalid contract file; {}".format(filename)) extra_data = contract_info.get('extra_data', {}) obj = cls(code, state, contract_info['contract_id'], contract_info['creator_id'], extra_data=extra_data) for enclave in contract_info['enclaves_info']: obj.set_state_encryption_key( enclave['contract_enclave_id'], enclave['encrypted_contract_state_encryption_key']) obj.set_replication_parameters(contract_info['num_provable_replicas'], contract_info['availability_duration']) return obj
class ContractResponse(object) : """ Class for managing the contract operation response from an enclave service """ __start_commit_service__ = True # ------------------------------------------------------- @staticmethod def exit_commit_workers(): """Set the global variable stop_commit_service to True. This will be picked by the workers""" if ContractResponse.__start_commit_service__ is False: #if True no service has yet been started stop_replication_service() stop_transacion_processing_service() # ------------------------------------------------------- def __init__(self, request, response) : """ Initialize a contract response object :param request: the ContractRequest object corresponding to the response :param response: diction containing the response from the enclave """ self.status = response['Status'] self.invocation_response_raw = response['InvocationResponse'] self.invocation_response = invocation_response(response['InvocationResponse']) self.state_changed = response['StateChanged'] self.new_state_object = request.contract_state #if the new state is same as the old state, then change set is empty self.new_state_object.changed_block_ids=[] self.request_number = request.request_number self.operation = request.operation if self.status and self.state_changed : self.signature = response['Signature'] state_hash_b64 = response['StateHash'] # we have another mismatch between the field names in the enclave # and the field names expected in the transaction; this needs to # be fixed at some point self.dependencies = [] for dependency in response['Dependencies'] : contract_id = dependency['ContractID'] state_hash = dependency['StateHash'] self.dependencies.append({'contract_id' : contract_id, 'state_hash' : state_hash}) # save the information we will need for the transaction self.channel_keys = request.channel_keys self.channel_id = request.channel_id self.contract_id = request.contract_id self.creator_id = request.creator_id self.code_hash = request.contract_code.compute_hash() self.message_hash = request.message.compute_hash() self.new_state_hash = crypto.base64_to_byte_array(state_hash_b64) self.originator_keys = request.originator_keys self.enclave_service = request.enclave_service self.old_state_hash = () if request.operation != 'initialize' : self.old_state_hash = ContractState.compute_hash(request.contract_state.raw_state) if not self.__verify_enclave_signature(request.enclave_keys) : raise Exception('failed to verify enclave signature') self.raw_state = self.enclave_service.get_block(state_hash_b64) self.new_state_object = ContractState(self.contract_id, self.raw_state) self.new_state_object.pull_state_from_eservice(self.enclave_service) # compute ids of blocks in the change set (used for replication) self.new_state_object.compute_ids_of_newblocks(request.contract_state.component_block_ids) self.replication_params = request.replication_params # ------------------------------------------------------- @property @deprecated def result(self): return self.invocation_response # ------------------------------------------------------- @property def commit_id(self): if self.status and self.state_changed: return (self.contract_id, self.new_state_hash, self.request_number) else: return None # ------------------------------------------------------- def commit_asynchronously(self, ledger_config=None, wait_parameter_for_ledger=30): """Commit includes two steps: First, replicate the change set to all provisioned encalves. Second, commit the transaction to the ledger. In this method, we add a job to the replication queue to enable the first step. The job will be picked by a replication worker thead. A call_back_after_replication function (see below) is automatically invoked to add a task for the second step """ #start threads for commiting response if not done before if ContractResponse.__start_commit_service__: # start replication service start_replication_service() start_transaction_processing_service() ContractResponse.__start_commit_service__ = False #create the replication request self.replication_request = ReplicationRequest(self.replication_params, self.contract_id, \ self.new_state_object.changed_block_ids, self.commit_id) #create the transaction request if ledger is enabled if ledger_config: self.transaction_request = TransactionRequest(ledger_config, self.commit_id, wait_parameter_for_ledger) else: self.transaction_request = None #submit the replication task add_replication_task(self) # ------------------------------------------------------- def call_back_after_replication(self): """this is the call back function after replication. Currently, the call-back's role is to add a new task to the pending transactions queue, which will be processed by a "submit transaction" thread whose job is to submit transactions corresponding to completed replication tasks """ if self.transaction_request: add_transaction_task(self) # ------------------------------------------------------- def wait_for_commit(self): """Wait for completion of the commit task corresponding to the response. Return transaction id if ledger is used, else return None""" # wait for the completion of the replication task try: self.replication_request.wait_for_completion() except Exception as e: raise Exception(str(e)) # wait for the completion of the transaction processing if ledger is in use if self.transaction_request: try: txn_id = self.transaction_request.wait_for_completion() except Exception as e: raise Exception(str(e)) else: txn_id = None return txn_id # ------------------------------------------------------- def __verify_enclave_signature(self, enclave_keys) : """verify the signature of the response """ message = self.__serialize_for_signing() return enclave_keys.verify(message, self.signature, encoding = 'b64') # ------------------------------------------------------- def __serialize_for_signing(self) : """serialize the response for enclave signature verification""" message = crypto.string_to_byte_array(self.channel_id) message += crypto.string_to_byte_array(self.contract_id) message += crypto.string_to_byte_array(self.creator_id) message += self.code_hash message += self.message_hash message += self.new_state_hash message += self.old_state_hash for dependency in self.dependencies : message += crypto.string_to_byte_array(dependency['contract_id']) message += crypto.string_to_byte_array(dependency['state_hash']) return message