예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
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
예제 #5
0
    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;
예제 #6
0
    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')
예제 #7
0
    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)
예제 #8
0
    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
예제 #9
0
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
예제 #10
0
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))
예제 #11
0
    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
예제 #12
0
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