async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received get acceptance request."""
        session = await context.session()
        ledger: BaseLedger = session.inject(BaseLedger, required=False)
        if not ledger or ledger.BACKEND_NAME != 'indy':
            report = ProblemReport(explain_ltxt='Invalid ledger.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        taa_info = await ledger.get_txn_author_agreement()
        acceptance = await ledger.get_latest_txn_author_acceptance()

        if taa_info['taa_required'] and not acceptance:
            needed = True
        elif acceptance and acceptance['digest'] != taa_info['taa_record'][
                'digest']:
            needed = True
        else:
            needed = False

        result = Acceptance(
            needed=needed,
            version=acceptance.get('version'),
            time=acceptance.get('time'),
            mechanism=acceptance.get('mechanism'),
        )
        result.assign_thread_from(context.message)
        await responder.send_reply(result)
Esempio n. 2
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        # Verify connection exists
        session = await context.session()
        manager = MediationManager(session)
        try:
            connection = await ConnRecord.retrieve_by_id(
                session, context.message.connection_id)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt='Connection not found.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        _record, request = await manager.prepare_request(
            connection.connection_id)
        # Send mediation request
        await responder.send(
            request,
            connection_id=connection.connection_id,
        )

        # Send notification of mediation request sent
        sent = MediationRequestSent(connection_id=connection.connection_id)
        sent.assign_thread_from(context.message)
        await responder.send_reply(sent)
Esempio n. 3
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received send requests."""
        # pylint: disable=protected-access
        try:
            connection = await ConnectionRecord.retrieve_by_id(
                context, context.message.connection_id)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt='Connection not found.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        msg = BasicMessage(content=context.message.content,
                           localization=LocalizationDecorator(locale='en'))

        await responder.send(msg, connection_id=connection.connection_id)

        record = BasicMessageRecord(
            connection_id=context.message.connection_id,
            message_id=msg._id,
            sent_time=msg.sent_time,
            content=msg.content,
            state=BasicMessageRecord.STATE_SENT)
        await record.save(context, reason='Message sent.')
        sent_msg = Sent(connection_id=connection.connection_id, message=record)
        sent_msg.assign_thread_from(context.message)
        await responder.send_reply(sent_msg)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received get request."""
        ledger: BaseLedger = await context.inject(BaseLedger, required=False)
        if not ledger or ledger.LEDGER_TYPE != 'indy':
            report = ProblemReport(explain_ltxt='Invalid ledger.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        taa_info = await ledger.get_txn_author_agreement()
        acceptance = await ledger.get_latest_txn_author_acceptance()

        if taa_info['taa_required'] and not acceptance:
            needed = True
        elif acceptance and acceptance['digest'] != taa_info['taa_record'][
                'digest']:
            needed = True
        else:
            needed = False

        result = Taa(version=taa_info['taa_record']['version'],
                     text=taa_info['taa_record']['text'],
                     needed=needed)
        result.assign_thread_from(context.message)
        await responder.send_reply(result)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle static connection get list request."""
        session = await context.session()
        connection_mgr = ConnectionManager(session)
        wallet: BaseWallet = session.inject(BaseWallet)
        try:
            tag_filter = dict(
                filter(
                    lambda item: item[1] is not None, {
                        'my_did': context.message.my_did,
                        'their_did': context.message.their_did,
                    }.items()))
            post_filter_positive = dict(
                filter(
                    lambda item: item[1] is not None, {
                        'initiator': context.message.initiator,
                        'invitation_key': context.message.invitation_key,
                        'invitation_mode': ConnRecord.INVITATION_MODE_STATIC,
                        'their_role': context.message.their_role
                    }.items()))
            records = await ConnRecord.query(
                session, tag_filter, post_filter_positive=post_filter_positive)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt='Connection not found.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        def flatten_target(connection, target, my_info):
            """Map for flattening results."""
            return {
                'connection_id': connection.connection_id,
                'their_info': {
                    'label': target.label,
                    'did': target.did,
                    'vk': target.recipient_keys[0],
                    'endpoint': target.endpoint
                },
                'my_info': {
                    'did': my_info.did,
                    'vk': my_info.verkey,
                    'endpoint': context.settings.get("default_endpoint")
                }
            }

        targets = []
        my_info = []
        for record in records:
            targets.extend(
                await connection_mgr.get_connection_targets(connection=record))
            my_info.append(await wallet.get_local_did(record.my_did))

        results = list(map(flatten_target, records, targets, my_info))

        static_connections = StaticConnectionList(results=results)
        static_connections.assign_thread_from(context.message)
        await responder.send_reply(static_connections)
async def setup(
        session: ProfileSession,
        protocol_registry: ProblemReport = None
):
    """Setup the admin-mediator v1_0 plugin."""
    if not protocol_registry:
        protocol_registry = session.inject(ProtocolRegistry)
    protocol_registry.register_message_types(
        MESSAGE_TYPES
    )
async def setup(
        context: InjectionContext,
        protocol_registry: ProblemReport = None
):
    """Setup the issuer plugin."""
    if not protocol_registry:
        protocol_registry = await context.inject(ProtocolRegistry)
    protocol_registry.register_message_types(
        MESSAGE_TYPES
    )
Esempio n. 8
0
        async def _wrapped(handler, context: RequestContext,
                           responder: BaseResponder):

            if not context.connection_record \
                    or context.connection_record.their_role != role:
                report = ProblemReport(
                    explain_ltxt='This connection is not authorized to perform'
                    ' the requested action.',
                    who_retries='none')
                report.assign_thread_from(context.message)
                await responder.send_reply(report)
                return

            return await func(handler, context, responder)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle taa acceptance messages."""
        ledger: BaseLedger = await context.inject(BaseLedger, required=False)
        if not ledger or ledger.LEDGER_TYPE != 'indy':
            report = ProblemReport(explain_ltxt='Invalid ledger.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        taa_record = {
            'version':
            context.message.version,
            'text':
            context.message.text,
            'digest':
            ledger.taa_digest(context.message.version, context.message.text)
        }
        try:
            await ledger.accept_txn_author_agreement(taa_record,
                                                     context.message.mechanism)
        except Exception as err:
            report = ProblemReport(
                explain_ltxt='An error occured while attempting to accept'
                ' the Transaction Author Agreement: {}'.format(err),
                who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        result = Accepted()
        result.assign_thread_from(context.message)
        await responder.send_reply(result)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received address list requests."""
        if context.message.method and context.message.method != SOV_METHOD:
            report = ProblemReport(
                explain_ltxt=(
                    'Method "{}" is not supported.'
                    .format(context.message.method)
                ),
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        session = await context.session()
        ledger: BaseLedger = session.inject(BaseLedger)
        try:
            addresses = json.loads(
                await payment.list_payment_addresses(ledger.wallet.handle)
            )

            async with ledger:
                address_results = []
                for address in addresses:
                    balance = 0
                    sources = []
                    async for source in get_sources(ledger, address):
                        balance += source['amount']
                        sources.append(source)
                    address_results.append({
                        'address': address,
                        'method': SOV_METHOD,
                        'balance': sovatoms_to_tokens(balance),
                        'raw_repr': {
                            'sources': sources
                        }
                    })
        except (LedgerError, PaymentError) as err:
            report = ProblemReport(
                explain_ltxt=(err),
                who_retries='none'
            )
            await responder.send_reply(report)
            return
        except IndyError as err:
            # TODO: Remove when IndyErrorHandler bug is fixed.
            # Likely to be next ACA-Py release.
            message = 'Unexpected IndyError while retrieving addresses'
            if hasattr(err, 'message'):
                message += ': {}'.format(err.message)
            report = ProblemReport(
                explain_ltxt=(message),
                who_retries='none'
            )
            await responder.send_reply(report)
            return

        result = AddressList(addresses=address_results)
        result.assign_thread_from(context.message)
        await responder.send_reply(result)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle delete connection request."""
        if context.message.connection_id == \
                context.connection_record.connection_id:

            report = ProblemReport(
                explain_ltxt='Current connection cannot be deleted.',
                who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        session = await context.session()
        try:
            connection = await ConnRecord.retrieve_by_id(
                session, context.message.connection_id)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt='Connection not found.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        await connection.delete_record(session)
        deleted = Deleted(connection_id=connection.connection_id)
        deleted.assign_thread_from(context.message)
        await responder.send_reply(deleted)
async def setup(
        session: ProfileSession,
        protocol_registry: ProblemReport = None
):
    """Load plugin."""

    # Load in libsovtoken
    cdll.LoadLibrary(LIBRARY).sovtoken_init()

    if not protocol_registry:
        protocol_registry = session.inject(ProtocolRegistry)
    protocol_registry.register_message_types(
        MESSAGE_TYPES
    )
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle payment"""
        session = await context.session()
        ledger: BaseLedger = session.inject(BaseLedger)
        if context.message.method != SOV_METHOD:
            report = ProblemReport(
                explain_ltxt=(
                    'Method "{}" is not supported.'
                    .format(context.message.method)
                ),
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        async with ledger:
            try:
                fee = (await get_transfer_auth(ledger))['price']
                inputs, outputs = await prepare_payment(
                    ledger,
                    context.message.from_address,
                    context.message.to_address,
                    tokens_to_sovatoms(context.message.amount),
                    fee
                )
                receipts = await make_payment(
                    ledger, inputs, outputs
                )
            except (LedgerError, PaymentError) as err:
                report = ProblemReport(
                    explain_ltxt=(err),
                    who_retries='none'
                )
                await responder.send_reply(report)
                return
            except IndyError as err:
                # TODO: Remove when IndyErrorHandler bug is fixed.
                # Likely to be next ACA-Py release.
                message = 'Unexpected IndyError while making payment'
                if hasattr(err, 'message'):
                    message += ': {}'.format(err.message)
                report = ProblemReport(
                    explain_ltxt=(message),
                    who_retries='none'
                )
                await responder.send_reply(report)
                return

        completed = TransferComplete(
            from_address=context.message.from_address,
            to_address=context.message.to_address,
            amount=context.message.amount,
            fees=sovatoms_to_tokens(fee),
            method=SOV_METHOD,
            raw_repr=receipts
        )
        completed.assign_thread_from(context.message)
        await responder.send_reply(completed)
Esempio n. 14
0
        async def _wrapped(handler, context: RequestContext,
                           responder: BaseResponder):
            if context.connection_record:
                session = await context.session()
                group = await context.connection_record.metadata_get(
                    session, 'group')
                if group == role:
                    return await func(handler, context, responder)

            report = ProblemReport(
                explain_ltxt='This connection is not authorized to perform'
                ' the requested action.',
                who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received presentation request request."""

        connection_id = str(context.message.connection_id)
        try:
            connection_record = await ConnectionRecord.retrieve_by_id(
                context,
                connection_id
            )
        except StorageNotFoundError:
            report = ProblemReport(
                explain_ltxt='Connection not found.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        if not connection_record.is_ready:
            report = ProblemReport(
                explain_ltxt='Connection invalid.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        comment = context.message.comment

        indy_proof_request = context.message.proof_request
        if not indy_proof_request.get('nonce'):
            indy_proof_request['nonce'] = str(uuid4().int)

        presentation_request_message = PresentationRequest(
            comment=comment,
            request_presentations_attach=[
                AttachDecorator.from_indy_dict(indy_proof_request)
            ]
        )

        presentation_manager = PresentationManager(context)

        presentation_exchange_record = (
            await presentation_manager.create_exchange_for_request(
                connection_id=connection_id,
                presentation_request_message=presentation_request_message
            )
        )

        await responder.send(
            presentation_request_message,
            connection_id=connection_id
        )

        pres_exchange = IssuerPresExchange(**presentation_exchange_record.serialize())
        pres_exchange.assign_thread_from(context.message)
        await responder.send_reply(pres_exchange)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle update connection request."""
        session = await context.session()
        try:
            connection = await ConnRecord.retrieve_by_id(
                session, context.message.connection_id)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt='Connection not found.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)

        new_label = context.message.label or connection.their_label
        connection.their_label = new_label
        await connection.save(session, reason="Update request received.")
        conn_response = Connection(**conn_record_to_message_repr(connection))
        conn_response.assign_thread_from(context.message)
        await responder.send_reply(conn_response)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received send request."""
        comment = context.message.comment
        connection_id = str(context.message.connection_id)
        preview_spec = context.message.credential_proposal

        try:
            connection_record = await ConnectionRecord.retrieve_by_id(
                context,
                connection_id
            )
        except StorageNotFoundError:
            report = ProblemReport(
                explain_ltxt='Connection not found.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        if not connection_record.is_ready:
            report = ProblemReport(
                explain_ltxt='Connection invalid.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        credential_proposal = CredentialProposal(
            comment=comment,
            credential_proposal=preview_spec,
            **{
                t: getattr(context.message, t)
                for t in CRED_DEF_TAGS if hasattr(context.message, t)
            },
        )

        credential_manager = CredentialManager(context)


        cred_exchange_record, cred_offer_message = \
            await credential_manager.prepare_send(
                connection_id,
                credential_proposal=credential_proposal
            )

        await responder.send(
            cred_offer_message,
            connection_id=cred_exchange_record.connection_id
        )
        cred_exchange = IssuerCredExchange(**cred_exchange_record.serialize())
        cred_exchange.assign_thread_from(context.message)
        await responder.send_reply(cred_exchange)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received send presentation proposal request."""

        connection_id = str(context.message.connection_id)
        try:
            connection_record = await ConnectionRecord.retrieve_by_id(
                context,
                connection_id
            )
        except StorageNotFoundError:
            report = ProblemReport(
                explain_ltxt='Connection not found.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        if not connection_record.is_ready:
            report = ProblemReport(
                explain_ltxt='Connection invalid.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        comment = context.message.comment
        # Aries#0037 calls it a proposal in the proposal struct but it's of type preview
        presentation_proposal = PresentationProposal(
            comment=comment,
            presentation_proposal=context.message.presentation_proposal
        )
        auto_present = (
            context.message.auto_present or
            context.settings.get("debug.auto_respond_presentation_request")
        )

        presentation_manager = PresentationManager(context)

        presentation_exchange_record = (
            await presentation_manager.create_exchange_for_proposal(
                connection_id=connection_id,
                presentation_proposal_message=presentation_proposal,
                auto_present=auto_present
            )
        )
        await responder.send(presentation_proposal, connection_id=connection_id)

        pres_exchange = PresExchange(**presentation_exchange_record.serialize())
        pres_exchange.assign_thread_from(context.message)
        await responder.send_reply(pres_exchange)
Esempio n. 19
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        storage: BaseStorage = await context.inject(BaseStorage)
        self._logger.debug("SCHEMA_EXCHANGE_RESPONSE called with context %s",
                           context)
        assert isinstance(context.message, Response)

        state = context.message.state
        payload = context.message.payload
        connection_id = context.connection_record.connection_id
        exchange_id = context.message.exchange_id
        hash_id = None

        try:
            request_record: SchemaExchangeRequestRecord = await SchemaExchangeRequestRecord.retrieve_by_exchange_id(
                context, exchange_id)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt="RequestRecordNotFound",
                                   who_retries="none")
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        request_record.state = state
        await request_record.save(context)

        if state == SchemaExchangeRequestRecord.STATE_APPROVED:

            # NOTE: Save the schema record to storage
            record: SchemaExchangeRecord = SchemaExchangeRecord(
                payload=payload,
                author=connection_id,
            )

            try:
                hash_id = await record.save(
                    context, reason="Saved, SchemaExchange from Other agent")
            except StorageDuplicateError:
                report = ProblemReport(explain_ltxt="Duplicate",
                                       who_retries="none")
                report.assign_thread_from(context.message)
                await responder.send_reply(report)
                return

        await responder.send_webhook(
            "schema_exchange",
            {
                "hash_id": hash_id,
                "connection_id": connection_id,
                "payload": payload,
                "state": state,
            },
        )
Esempio n. 20
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received send cred def request."""
        session = await context.session()
        ledger: BaseLedger = session.inject(BaseLedger)
        issuer: IndyIssuer = session.inject(IndyIssuer)
        # If no schema record, make one
        try:
            schema_record = await SchemaRecord.retrieve_by_schema_id(
                session, schema_id=context.message.schema_id)
        except StorageNotFoundError:
            # Schema will be cached so retrieving here is not
            # any less efficient (schema is retrieved in
            # send_credential_definition).
            async with ledger:
                schema = await ledger.get_schema(context.message.schema_id)

            schema_record = SchemaRecord(schema_id=schema['id'],
                                         schema_name=schema['name'],
                                         schema_version=schema['version'],
                                         attributes=schema['attrNames'],
                                         state=SchemaRecord.STATE_WRITTEN,
                                         author=SchemaRecord.AUTHOR_OTHER)
            await schema_record.save(session, reason='Retrieved from ledger')

        try:
            async with ledger:
                credential_definition_id, *_ = await shield(
                    ledger.create_and_send_credential_definition(
                        issuer,
                        context.message.schema_id,
                        tag='{}_{}'.format(schema_record.schema_name,
                                           schema_record.schema_version)))
        except Exception as err:
            report = ProblemReport(
                explain_ltxt='Failed to send to ledger; Error: {}'.format(err),
                who_retries='none')
            LOGGER.exception("Failed to send cred def to ledger: %s", err)
            await responder.send_reply(report)
            return

        # we may not need to save the record as below
        cred_def_record = CredDefRecord(cred_def_id=credential_definition_id,
                                        schema_id=context.message.schema_id,
                                        attributes=list(
                                            map(canon,
                                                schema_record.attributes)),
                                        state=CredDefRecord.STATE_WRITTEN,
                                        author=CredDefRecord.AUTHOR_SELF)
        await cred_def_record.save(
            session, reason="Committed credential definition to ledger")

        result = CredDefID(cred_def_id=credential_definition_id)
        result.assign_thread_from(context.message)
        await responder.send_reply(result)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received send proposal request."""
        connection_id = str(context.message.connection_id)
        credential_definition_id = context.message.credential_definition_id
        comment = context.message.comment

        credential_manager = CredentialManager(context)

        try:
            connection_record = await ConnectionRecord.retrieve_by_id(
                context,
                connection_id
            )
        except StorageNotFoundError:
            report = ProblemReport(
                explain_ltxt='Connection not found.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        if not connection_record.is_ready:
            report = ProblemReport(
                explain_ltxt='Connection invalid.',
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        credential_exchange_record = await credential_manager.create_proposal(
            connection_id,
            comment=comment,
            credential_preview=context.message.credential_proposal,
            credential_definition_id=credential_definition_id
        )

        await responder.send(
            CredentialProposal(
                comment=context.message.comment,
                credential_proposal=context.message.credential_proposal,
                cred_def_id=context.message.credential_definition_id
            ),
            connection_id=connection_id
        )
        cred_exchange = CredExchange(**credential_exchange_record.serialize())
        cred_exchange.assign_thread_from(context.message)
        await responder.send_reply(cred_exchange)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle get fees."""
        session = await context.session()
        ledger: BaseLedger = session.inject(BaseLedger)
        if context.message.method != SOV_METHOD:
            report = ProblemReport(
                explain_ltxt=(
                    'Method "{}" is not supported.'
                    .format(context.message.method)
                ),
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        try:
            async with ledger:
                xfer_auth = await get_transfer_auth(ledger)
        except (LedgerError, PaymentError) as err:
            report = ProblemReport(
                explain_ltxt=(err),
                who_retries='none'
            )
            await responder.send_reply(report)
            return
        except IndyError as err:
            # TODO: Remove when IndyErrorHandler bug is fixed.
            # Likely to be next ACA-Py release.
            message = 'Unexpected IndyError while retrieving transfer fee'
            if hasattr(err, 'message'):
                message += ': {}'.format(err.message)
            report = ProblemReport(
                explain_ltxt=(message),
                who_retries='none'
            )
            await responder.send_reply(report)
            return

        fees = Fees(total=sovatoms_to_tokens(xfer_auth['price']))
        fees.assign_thread_from(context.message)
        await responder.send_reply(fees)
Esempio n. 23
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        storage: BaseStorage = await context.inject(BaseStorage)

        self._logger.debug(
            "SCHEMA_EXCHANGE Request called with context %s, \n\nRESPONDER: %s",
            context,
            responder,
        )
        assert isinstance(context.message, Request)

        state = SchemaExchangeRequestRecord.STATE_DENIED
        try:
            record = await SchemaExchangeRecord.retrieve_by_id(
                context, context.message.hash_id)
            state = SchemaExchangeRequestRecord.STATE_APPROVED
        except StorageNotFoundError:
            response = Response(
                state=state,
                exchange_id=context.message.exchange_id,
                payload="STORAGE NOT FOUND",
            )
            response.assign_thread_from(context.message)
            await responder.send_reply(response)

        request_record = SchemaExchangeRequestRecord(
            payload=context.message.hash_id,
            author=SchemaExchangeRequestRecord.AUTHOR_OTHER,
            state=state,
            connection_id=context.connection_record.connection_id,
            exchange_id=context.message.exchange_id,
        )

        try:
            await request_record.save(context,
                                      reason="Saved, Request from Other agent")
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt="StorageNotFound",
                                   who_retries="none")
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        await responder.send_webhook(
            "schema_exchange",
            {
                "hash_id": context.message.hash_id,
                "connection_id": context.connection_record.connection_id,
                "state": state,
                "exchange_id": context.message.exchange_id,
                "payload": record.payload,
            },
        )

        if state == SchemaExchangeRequestRecord.STATE_APPROVED:
            response = Response(
                state=state,
                exchange_id=context.message.exchange_id,
                payload=record.payload,
            )
            response.assign_thread_from(context.message)
            await responder.send_reply(response)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received create address requests."""
        session = await context.session()
        wallet: BaseWallet = session.inject(BaseWallet)
        ledger: BaseLedger = session.inject(BaseLedger)
        if context.message.method != SOV_METHOD:
            report = ProblemReport(
                explain_ltxt=(
                    'Method "{}" is not supported.'
                    .format(context.message.method)
                ),
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        if context.message.seed and len(context.message.seed) < 32:
            report = ProblemReport(
                explain_ltxt=(
                    'Seed must be 32 characters in length'
                ),
                who_retries='none'
            )
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return


        try:
            address_str = await payment.create_payment_address(
                wallet.handle,
                SOV_METHOD,
                json.dumps({
                    'seed': context.message.seed
                })
            )
        except IndyError as err:
            message = 'Failed to create payment address'
            if hasattr(err, 'message'):
                message += ': {}'.format(err.message)
            report = ProblemReport(
                explain_ltxt=(message),
                who_retries='none'
            )
            await responder.send_reply(report)
            return


        try:
            async with ledger:
                balance = 0
                sources = []
                async for source in get_sources(ledger, address_str):
                    balance += source['amount']
                    sources.append(source)
        except (LedgerError, PaymentError) as err:
            report = ProblemReport(
                explain_ltxt=(err),
                who_retries='none'
            )
            await responder.send_reply(report)
            return
        except IndyError as err:
            # TODO: Remove when IndyErrorHandler bug is fixed.
            # Likely to be next ACA-Py release.
            message = 'Unexpected IndyError while retrieving address balances'
            if hasattr(err, 'message'):
                message += ': {}'.format(err.message)
            report = ProblemReport(
                explain_ltxt=(message),
                who_retries='none'
            )
            await responder.send_reply(report)
            return

        address = Address(
            address=address_str,
            method=SOV_METHOD,
            balance=sovatoms_to_tokens(balance),
        )
        address.assign_thread_from(context.message)
        await responder.send_reply(address)
        return