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)
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.state_changed = False self.status = response['Status'] self.invocation_response_raw = response['InvocationResponse'] self.invocation_response = invocation_response( response['InvocationResponse']) self.new_state_object = request.contract_state self.new_state_object.changed_block_ids = [] 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.originator_keys = request.originator_keys self.enclave_service = request.enclave_service self.dependencies = [] # ------------------------------------------------------- @property @deprecated def result(self): return self.invocation_response # ------------------------------------------------------- @property def commit_id(self): return (self.contract_id, self.new_state_hash, self.request_number) # ------------------------------------------------------- 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, message, enclave_keys): """verify the signature of the response """ 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 += self.code_hash message += self.message_hash return message
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