예제 #1
0
def send_to_contract(state,
                     save_file,
                     enclave,
                     message,
                     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 ----------
    try:
        enclave_client = get_enclave_service(state, enclave)
    except Exception as e:
        raise Exception('unable to connect to enclave service; {0}'.format(
            str(e)))

    try:
        # this is just a sanity check to make sure the selected enclave
        # has actually been provisioned
        contract.get_state_encryption_key(enclave_client.enclave_id)
    except KeyError as ke:
        logger.error('selected enclave is not provisioned')
        sys.exit(-1)

    # ---------- send the message to the enclave service ----------
    try:
        update_request = contract.create_update_request(
            client_keys, enclave_client, message)
        update_response = update_request.evaluate()
        if update_response.status:
            if not quiet: print(update_response.result)
        else:
            print('ERROR: {}'.format(update_response.result))
            return None
    except Exception as e:
        raise Exception('enclave failed to evaluation expression; {0}'.format(
            str(e)))

    data_directory = state.get(['Contract', 'DataDirectory'])
    ledger_config = state.get(['Sawtooth'])

    if update_response.state_changed and commit:
        try:
            logger.debug("send update to the ledger")
            extraparams = {}
            if wait:
                extraparams['wait'] = 30
            txnid = update_response.submit_update_transaction(
                ledger_config, **extraparams)
        except Exception as e:
            raise Exception(
                'failed to save the new state to the ledger; {0}'.format(
                    str(e)))

        try:
            contract.set_state(update_response.encrypted_state)
            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.result
예제 #2
0
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