Example #1
0
def parse_request_args(request: Request) -> Dict:
    asset = Asset.objects.filter(code=request.GET.get("asset_code"),
                                 sep6_enabled=True,
                                 deposit_enabled=True).first()
    if not asset:
        return {"error": render_error_response(_("invalid 'asset_code'"))}

    lang = request.GET.get("lang")
    if lang:
        err_resp = validate_language(lang)
        if err_resp:
            return {"error": err_resp}
        activate_lang_for_request(lang)

    memo_type = request.GET.get("memo_type")
    if memo_type and memo_type not in Transaction.MEMO_TYPES:
        return {"error": render_error_response(_("invalid 'memo_type'"))}

    try:
        memo = memo_str(request.GET.get("memo"), memo_type)
    except (ValueError, MemoInvalidException):
        return {
            "error": render_error_response(_("invalid 'memo' for 'memo_type'"))
        }

    return {
        "asset": asset,
        "memo_type": memo_type,
        "memo": memo,
        "lang": lang,
        "type": request.GET.get("type"),
        **extract_sep9_fields(request.GET),
    }
Example #2
0
def test_memo_str_hash_memo():
    raw_bytes = token_bytes(32)
    memo_str = utils.memo_hex_to_base64(raw_bytes.hex())
    assert utils.memo_str(HashMemo(raw_bytes)) == (
        memo_str,
        Transaction.MEMO_TYPES.hash,
    )
    def get(account: str, request: Request) -> Response:
        if request.GET.get(
                "account") and account != request.GET.get("account"):
            return render_error_response(
                _("The account specified does not match authorization token"),
                status_code=403,
            )
        elif not (request.GET.get("id") or request.GET.get("account")):
            return render_error_response(
                _("unable to identify a user without 'id' or 'account'"))
        elif request.GET.get("memo_type") and not request.GET.get("memo"):
            return render_error_response(_("missing 'memo' for 'memo_type'"))

        try:
            # validate memo and memo_type
            memo_str(request.GET.get("memo"), request.GET.get("memo_type"))
        except ValueError:
            return render_error_response(_("invalid 'memo' for 'memo_type'"))

        try:
            response_data = rci.get({
                "id": request.GET.get("id"),
                "sep10_client_account": account,
                "account": request.GET.get("account"),
                "memo": request.GET.get("memo"),
                "memo_type": request.GET.get("memo_type"),
                "type": request.GET.get("type"),
                "lang": request.GET.get("lang"),
            })
        except ValueError as e:
            return render_error_response(str(e), status_code=400)
        except ObjectDoesNotExist as e:
            return render_error_response(str(e), status_code=404)

        try:
            validate_response_data(response_data)
        except ValueError:
            logger.exception(
                _("An exception was raised validating GET /customer response"))
            return render_error_response(_("unable to process request."),
                                         status_code=500)

        return Response(response_data)
    def put(account: str, request: Request) -> Response:
        if request.data.get("id") and not request.data.get("account"):
            if not isinstance(request.data.get("id"), str):
                return render_error_response(_("bad ID value, expected str"))
        elif account != request.data.get("account"):
            return render_error_response(
                _("The account specified does not match authorization token"),
                status_code=403,
            )
        elif request.data.get("memo_type") and not request.data.get("memo"):
            return render_error_response(_("missing 'memo' for 'memo_type'"))

        if request.data.get("memo"):
            try:
                # validate memo and memo_type
                memo_str(request.data.get("memo"),
                         request.data.get("memo_type"))
            except ValueError:
                return render_error_response(
                    _("invalid 'memo' for 'memo_type'"))

        try:
            customer_id = rci.put({
                "id": request.data.get("id"),
                "account": account,
                "memo": request.data.get("memo"),
                "memo_type": request.data.get("memo_type"),
                **extract_sep9_fields(request.data),
            })
        except ValueError as e:
            return render_error_response(str(e), status_code=400)
        except ObjectDoesNotExist as e:
            return render_error_response(str(e), status_code=404)

        if not isinstance(customer_id, str):
            logger.error(
                "Invalid customer ID returned from put() integration. Must be str."
            )
            return render_error_response(_("unable to process request"))

        return Response({"id": customer_id}, status=202)
def delete(account_from_auth: str, request: Request, account: str) -> Response:
    if account_from_auth != account:
        return render_error_response("account not found", status_code=404)
    try:
        memo = memo_str(request.data.get("memo"),
                        request.data.get("memo_type"))
    except ValueError as e:
        return render_error_response("invalid 'memo' for 'memo_type'")
    try:
        rci.delete(account, memo, request.data.get("memo_type"))
    except ValueError:
        return render_error_response("account not found", status_code=404)
    else:
        return Response({}, status=200)
def parse_request_args(request: Request) -> Dict:
    asset = Asset.objects.filter(code=request.GET.get("asset_code"),
                                 sep6_enabled=True,
                                 withdrawal_enabled=True).first()
    if not asset:
        return {"error": render_error_response(_("invalid 'asset_code'"))}

    lang = request.GET.get("lang")
    if lang:
        err_resp = validate_language(lang)
        if err_resp:
            return {"error": err_resp}
        activate_lang_for_request(lang)

    memo_type = request.GET.get("memo_type")
    if memo_type and memo_type not in Transaction.MEMO_TYPES:
        return {"error": render_error_response(_("invalid 'memo_type'"))}

    try:
        memo = memo_str(request.GET.get("memo"), memo_type)
    except (ValueError, MemoInvalidException):
        return {
            "error": render_error_response(_("invalid 'memo' for 'memo_type'"))
        }

    if not request.GET.get("type"):
        return {"error": render_error_response(_("'type' is required"))}
    if not request.GET.get("dest"):
        return {"error": render_error_response(_("'dest' is required"))}

    args = {
        "asset": asset,
        "memo_type": memo_type,
        "memo": memo,
        "lang": request.GET.get("lang"),
        "type": request.GET.get("type"),
        "dest": request.GET.get("dest"),
        "dest_extra": request.GET.get("dest_extra"),
        **extract_sep9_fields(request.GET),
    }

    # add remaining extra params, it's on the anchor to validate them
    for param, value in request.GET.items():
        if param not in args:
            args[param] = value

    return args
Example #7
0
def deposit(account: str, request: Request) -> Response:
    """
    POST /transactions/deposit/interactive

    Creates an `incomplete` deposit Transaction object in the database and
    returns the URL entry-point for the interactive flow.
    """
    asset_code = request.POST.get("asset_code")
    stellar_account = request.POST.get("account")
    lang = request.POST.get("lang")
    sep9_fields = extract_sep9_fields(request.POST)
    if lang:
        err_resp = validate_language(lang)
        if err_resp:
            return err_resp
        activate_lang_for_request(lang)

    # Verify that the request is valid.
    if not all([asset_code, stellar_account]):
        return render_error_response(
            _("`asset_code` and `account` are required parameters"))

    # Ensure memo won't cause stellar transaction to fail when submitted
    try:
        memo = memo_str(request.POST.get("memo"),
                        request.POST.get("memo_type"))
    except ValueError:
        return render_error_response(_("invalid 'memo' for 'memo_type'"))

    amount = None
    if request.POST.get("amount"):
        try:
            amount = Decimal(request.POST.get("amount"))
        except DecimalException as e:
            return render_error_response(_("Invalid 'amount'"))

    # Verify that the asset code exists in our database, with deposit enabled.
    asset = Asset.objects.filter(code=asset_code).first()
    if not asset:
        return render_error_response(_("unknown asset: %s") % asset_code)
    elif not (asset.deposit_enabled and asset.sep24_enabled):
        return render_error_response(
            _("invalid operation for asset %s") % asset_code)

    try:
        Keypair.from_public_key(stellar_account)
    except Ed25519PublicKeyInvalidError:
        return render_error_response(_("invalid 'account'"))

    try:
        rdi.save_sep9_fields(stellar_account, sep9_fields, lang)
    except ValueError as e:
        # The anchor found a validation error in the sep-9 fields POSTed by
        # the wallet. The error string returned should be in the language
        # specified in the request.
        return render_error_response(str(e))

    # Construct interactive deposit pop-up URL.
    transaction_id = create_transaction_id()
    Transaction.objects.create(
        id=transaction_id,
        stellar_account=account,
        asset=asset,
        kind=Transaction.KIND.deposit,
        status=Transaction.STATUS.incomplete,
        to_address=account,
        protocol=Transaction.PROTOCOL.sep24,
        memo=memo,
        memo_type=request.POST.get("memo_type") or Transaction.MEMO_TYPES.hash,
    )
    logger.info(f"Created deposit transaction {transaction_id}")

    url = interactive_url(
        request,
        str(transaction_id),
        account,
        asset_code,
        settings.OPERATION_DEPOSIT,
        amount,
    )
    return Response(
        {
            "type": "interactive_customer_info_needed",
            "url": url,
            "id": transaction_id
        },
        status=status.HTTP_200_OK,
    )
Example #8
0
def test_memo_str_text_memo():
    assert utils.memo_str(TextMemo("test")) == ("test",
                                                Transaction.MEMO_TYPES.text)
Example #9
0
def test_memo_str_bad_input_type():
    with pytest.raises(ValueError):
        utils.memo_str(Mock())
Example #10
0
def test_memo_str_none():
    assert utils.memo_str(None) == (None, None)
Example #11
0
def test_memo_str_id_memo():
    assert utils.memo_str(IdMemo(123)) == ("123", Transaction.MEMO_TYPES.id)