Exemple #1
0
async def lnurl_callback(track_id):
    track = await get_track(track_id)
    if not track:
        return jsonify({"status": "ERROR", "reason": "Couldn't find track."})

    amount_received = int(request.args.get("amount"))

    if amount_received < track.min_sendable:
        return (jsonify(
            LnurlErrorResponse(
                reason=
                f"Amount {round(amount_received / 1000)} is smaller than minimum {math.floor(track.min_sendable)}."
            ).dict()), )
    elif track.max_sendable < amount_received:
        return (jsonify(
            LnurlErrorResponse(
                reason=
                f"Amount {round(amount_received / 1000)} is greater than maximum {math.floor(track.max_sendable)}."
            ).dict()), )

    comment = request.args.get("comment")
    if len(comment or "") > 300:
        return jsonify(
            LnurlErrorResponse(
                reason=
                f"Got a comment with {len(comment)} characters, but can only accept 300"
            ).dict())

    ls = await get_livestream_by_track(track_id)

    payment_hash, payment_request = await create_invoice(
        wallet_id=ls.wallet,
        amount=int(amount_received / 1000),
        memo=await track.fullname(),
        description_hash=hashlib.sha256(
            (await track.lnurlpay_metadata()).encode("utf-8")).digest(),
        extra={
            "tag": "livestream",
            "track": track.id,
            "comment": comment
        },
    )

    if amount_received < track.price_msat:
        success_action = None
    else:
        success_action = track.success_action(payment_hash)

    resp = LnurlPayActionResponse(
        pr=payment_request,
        success_action=success_action,
        routes=[],
    )

    return jsonify(resp.dict())
Exemple #2
0
async def api_lnurl_callback(link_id):
    link = increment_pay_link(link_id, served_pr=1)
    if not link:
        return jsonify({"status": "ERROR", "reason": "LNURL-pay not found."}), HTTPStatus.OK

    min, max = link.min, link.max
    rate = await get_fiat_rate(link.currency) if link.currency else 1
    if link.currency:
        # allow some fluctuation (as the fiat price may have changed between the calls)
        min = rate * 995 * link.min
        max = rate * 1010 * link.max
    else:
        min = link.min * 1000
        max = link.max * 1000

    amount_received = int(request.args.get("amount"))
    if amount_received < min:
        return (
            jsonify(LnurlErrorResponse(reason=f"Amount {amount_received} is smaller than minimum {min}.").dict()),
            HTTPStatus.OK,
        )
    elif amount_received > max:
        return (
            jsonify(LnurlErrorResponse(reason=f"Amount {amount_received} is greater than maximum {max}.").dict()),
            HTTPStatus.OK,
        )

    comment = request.args.get("comment")
    if len(comment or "") > link.comment_chars:
        return (
            jsonify(
                LnurlErrorResponse(
                    reason=f"Got a comment with {len(comment)} characters, but can only accept {link.comment_chars}"
                ).dict()
            ),
            HTTPStatus.OK,
        )

    payment_hash, payment_request = create_invoice(
        wallet_id=link.wallet,
        amount=int(amount_received / 1000),
        memo=link.description,
        description_hash=hashlib.sha256(link.lnurlpay_metadata.encode("utf-8")).digest(),
        extra={"tag": "lnurlp", "link": link.id, "comment": comment},
    )

    resp = LnurlPayActionResponse(
        pr=payment_request,
        success_action=link.success_action(payment_hash),
        routes=[],
    )

    return jsonify(resp.dict()), HTTPStatus.OK
Exemple #3
0
async def lnurl_callback(cp_id):
    cp = await get_copilot(cp_id)
    if not cp:
        return jsonify({"status": "ERROR", "reason": "Copilot not found."})

    amount_received = int(request.args.get("amount"))

    if amount_received < 10000:
        return (jsonify(
            LnurlErrorResponse(
                reason=
                f"Amount {round(amount_received / 1000)} is smaller than minimum 10 sats."
            ).dict()), )
    elif amount_received / 1000 > 10000000:
        return (jsonify(
            LnurlErrorResponse(
                reason=
                f"Amount {round(amount_received / 1000)} is greater than maximum 50000."
            ).dict()), )
    comment = ""
    if request.args.get("comment"):
        comment = request.args.get("comment")
        if len(comment or "") > 300:
            return jsonify(
                LnurlErrorResponse(
                    reason=
                    f"Got a comment with {len(comment)} characters, but can only accept 300"
                ).dict())
        if len(comment) < 1:
            comment = "none"

    payment_hash, payment_request = await create_invoice(
        wallet_id=cp.wallet,
        amount=int(amount_received / 1000),
        memo=cp.lnurl_title,
        description_hash=hashlib.sha256(
            (LnurlPayMetadata(json.dumps([["text/plain",
                                           str(cp.lnurl_title)]
                                          ]))).encode("utf-8")).digest(),
        extra={
            "tag": "copilot",
            "copilot": cp.id,
            "comment": comment
        },
    )
    resp = LnurlPayActionResponse(
        pr=payment_request,
        success_action=None,
        disposable=False,
        routes=[],
    )
    return jsonify(resp.dict())
Exemple #4
0
async def lnurl_callback(item_id):
    item = await get_item(item_id)
    if not item:
        return jsonify({"status": "ERROR", "reason": "Couldn't find item."})

    if item.unit == "sat":
        min = item.price * 1000
        max = item.price * 1000
    else:
        price = await fiat_amount_as_satoshis(item.price, item.unit)
        # allow some fluctuation (the fiat price may have changed between the calls)
        min = price * 995
        max = price * 1010

    amount_received = int(request.args.get("amount") or 0)
    if amount_received < min:
        return jsonify(
            LnurlErrorResponse(
                reason=f"Amount {amount_received} is smaller than minimum {min}."
            ).dict())
    elif amount_received > max:
        return jsonify(
            LnurlErrorResponse(
                reason=f"Amount {amount_received} is greater than maximum {max}."
            ).dict())

    shop = await get_shop(item.shop)

    try:
        payment_hash, payment_request = await create_invoice(
            wallet_id=shop.wallet,
            amount=int(amount_received / 1000),
            memo=item.name,
            description_hash=hashlib.sha256(
                (await item.lnurlpay_metadata()).encode("utf-8")).digest(),
            extra={
                "tag": "offlineshop",
                "item": item.id
            },
        )
    except Exception as exc:
        return jsonify(LnurlErrorResponse(reason=exc.message).dict())

    resp = LnurlPayActionResponse(
        pr=payment_request,
        success_action=item.success_action(shop, payment_hash)
        if shop.method else None,
        routes=[],
    )

    return jsonify(resp.dict())
Exemple #5
0
async def perform_lnurlauth(
    callback: str,
    conn: Optional[Connection] = None,
) -> Optional[LnurlErrorResponse]:
    cb = urlparse(callback)

    k1 = unhexlify(parse_qs(cb.query)["k1"][0])
    key = g.wallet.lnurlauth_key(cb.netloc)

    def int_to_bytes_suitable_der(x: int) -> bytes:
        """for strict DER we need to encode the integer with some quirks"""
        b = x.to_bytes((x.bit_length() + 7) // 8, "big")

        if len(b) == 0:
            # ensure there's at least one byte when the int is zero
            return bytes([0])

        if b[0] & 0x80 != 0:
            # ensure it doesn't start with a 0x80 and so it isn't
            # interpreted as a negative number
            return bytes([0]) + b

        return b

    def encode_strict_der(r_int, s_int, order):
        # if s > order/2 verification will fail sometimes
        # so we must fix it here (see https://github.com/indutny/elliptic/blob/e71b2d9359c5fe9437fbf46f1f05096de447de57/lib/elliptic/ec/index.js#L146-L147)
        if s_int > order // 2:
            s_int = order - s_int

        # now we do the strict DER encoding copied from
        # https://github.com/KiriKiri/bip66 (without any checks)
        r = int_to_bytes_suitable_der(r_int)
        s = int_to_bytes_suitable_der(s_int)

        r_len = len(r)
        s_len = len(s)
        sign_len = 6 + r_len + s_len

        signature = BytesIO()
        signature.write(0x30.to_bytes(1, "big", signed=False))
        signature.write((sign_len - 2).to_bytes(1, "big", signed=False))
        signature.write(0x02.to_bytes(1, "big", signed=False))
        signature.write(r_len.to_bytes(1, "big", signed=False))
        signature.write(r)
        signature.write(0x02.to_bytes(1, "big", signed=False))
        signature.write(s_len.to_bytes(1, "big", signed=False))
        signature.write(s)

        return signature.getvalue()

    sig = key.sign_digest_deterministic(k1, sigencode=encode_strict_der)

    async with httpx.AsyncClient() as client:
        r = await client.get(
            callback,
            params={
                "k1": k1.hex(),
                "key": key.verifying_key.to_string("compressed").hex(),
                "sig": sig.hex(),
            },
        )
        try:
            resp = json.loads(r.text)
            if resp["status"] == "OK":
                return None

            return LnurlErrorResponse(reason=resp["reason"])
        except (KeyError, json.decoder.JSONDecodeError):
            return LnurlErrorResponse(reason=r.text[:200] +
                                      "..." if len(r.text) > 200 else r.text, )
Exemple #6
0
async def api_lnurlp_callback(link_id):
    link = await get_satsdice_pay(link_id)
    if not link:
        return (
            jsonify({
                "status": "ERROR",
                "reason": "LNUeL-pay not found."
            }),
            HTTPStatus.OK,
        )

    min, max = link.min_bet, link.max_bet
    min = link.min_bet * 1000
    max = link.max_bet * 1000

    amount_received = int(request.args.get("amount") or 0)
    if amount_received < min:
        return (
            jsonify(
                LnurlErrorResponse(
                    reason=
                    f"Amount {amount_received} is smaller than minimum {min}."
                ).dict()),
            HTTPStatus.OK,
        )
    elif amount_received > max:
        return (
            jsonify(
                LnurlErrorResponse(
                    reason=
                    f"Amount {amount_received} is greater than maximum {max}."
                ).dict()),
            HTTPStatus.OK,
        )

    payment_hash, payment_request = await create_invoice(
        wallet_id=link.wallet,
        amount=int(amount_received / 1000),
        memo="Satsdice bet",
        description_hash=hashlib.sha256(
            link.lnurlpay_metadata.encode("utf-8")).digest(),
        extra={
            "tag": "satsdice",
            "link": link.id,
            "comment": "comment"
        },
    )

    success_action = link.success_action(payment_hash)
    link = await create_satsdice_payment(satsdice_pay=link.id,
                                         value=amount_received / 1000,
                                         payment_hash=payment_hash)
    if success_action:
        resp = LnurlPayActionResponse(
            pr=payment_request,
            success_action=success_action,
            routes=[],
        )
    else:
        resp = LnurlPayActionResponse(
            pr=payment_request,
            routes=[],
        )

    return jsonify(resp.dict()), HTTPStatus.OK