feat: implement decode pay req endpoint

closes #16
This commit is contained in:
Stefan Stammberger 2021-09-20 17:00:36 +02:00
parent 43783549d1
commit ffdfe9fed6
No known key found for this signature in database
GPG key ID: 645FA807E935D9D5
5 changed files with 114 additions and 28 deletions

View file

@ -48,25 +48,25 @@ class Feature(BaseModel):
is_required: bool
is_known: bool
def feature_from_grpc(f):
return Feature(
name=f.name,
is_required=f.is_required,
is_known=f.is_known,
)
@classmethod
def from_grpc(cls, f):
return cls(
name=f.name,
is_required=f.is_required,
is_known=f.is_known,
)
class FeaturesEntry(BaseModel):
key: int
value: Feature
def features_entry_from_grpc(entry_key, feature):
return FeaturesEntry(
key=entry_key,
value=feature_from_grpc(feature),
)
@classmethod
def from_grpc(cls, entry_key, feature):
return cls(
key=entry_key,
value=Feature.from_grpc(feature),
)
class AMP(BaseModel):
@ -187,15 +187,15 @@ class RouteHint(BaseModel):
# The time-lock delta of the channel.
cltv_expiry_delta: int
def route_hint_from_grpc(h) -> RouteHint:
return RouteHint(
node_id=h.node_id,
chan_id=h.chan_id,
fee_base_msat=h.fee_base_msat,
fee_proportional_millionths=h.fee_proportional_millionths,
cltv_expiry_delta=h.cltv_expiry_delta,
)
@classmethod
def from_grpc(cls, h) -> "RouteHint":
return cls(
node_id=h.node_id,
chan_id=h.chan_id,
fee_base_msat=h.fee_base_msat,
fee_proportional_millionths=h.fee_proportional_millionths,
cltv_expiry_delta=h.cltv_expiry_delta,
)
class Invoice(BaseModel):
@ -311,7 +311,7 @@ def invoice_from_grpc(i) -> Invoice:
def _route_hints(hints):
l = []
for h in hints:
l.append(route_hint_from_grpc(h))
l.append(RouteHint.from_grpc((h)))
return l
def _htlcs(htlcs):
@ -323,7 +323,7 @@ def invoice_from_grpc(i) -> Invoice:
def _features(features):
l = []
for k in features:
l.append(features_entry_from_grpc(k, features[k]))
l.append(FeaturesEntry.from_grpc(k, features[k]))
return l
return Invoice(
@ -833,7 +833,7 @@ def ln_info_from_grpc(i) -> LnInfo:
_features = []
for f in i.features:
_features.append(features_entry_from_grpc(f, i.features[f]))
_features.append(FeaturesEntry.from_grpc(f, i.features[f]))
_uris = [u for u in i.uris]
@ -935,3 +935,37 @@ class WalletBalance(BaseModel):
channel_pending_open_local_balance=channel.pending_open_local_balance.msat,
channel_pending_open_remote_balance=channel.pending_open_remote_balance.msat,
)
class PaymentRequest(BaseModel):
destination: str
payment_hash: str
num_satoshis: int
timestamp: int
expiry: int
description: str
description_hash: str
fallback_addr: Optional[str]
cltv_expiry: int
route_hints: List[RouteHint] = Query([])
payment_addr: str = Query(..., description="The payment address in hex format")
num_msat: int
features: List[FeaturesEntry] = Query([])
@classmethod
def from_grpc(cls, r):
return cls(
destination=r.destination,
payment_hash=r.payment_hash,
num_satoshis=r.num_satoshis,
timestamp=r.timestamp,
expiry=r.expiry,
description=r.description,
description_hash=r.description_hash,
fallback_addr=r.fallback_addr,
cltv_expiry=r.cltv_expiry,
route_hints=[RouteHint.from_grpc(rh) for rh in r.route_hints],
payment_addr=r.payment_addr.hex(),
num_msat=r.num_msat,
features=[FeaturesEntry.from_grpc(k, r.features[k]) for k in r.features],
)

View file

@ -1,12 +1,19 @@
import asyncio
from app.models.lightning import Invoice, LightningStatus, LnInfo, Payment
from app.models.lightning import (
Invoice,
LightningStatus,
LnInfo,
Payment,
PaymentRequest,
)
from app.utils import SSE, lightning_config, send_sse_message
from decouple import config
if lightning_config.ln_node == "lnd":
from app.repositories.ln_impl.lnd import (
add_invoice_impl,
decode_pay_request_impl,
get_implementation_name,
get_ln_info_impl,
get_wallet_balance_impl,
@ -16,6 +23,7 @@ if lightning_config.ln_node == "lnd":
else:
from app.repositories.ln_impl.clightning import (
add_invoice_impl,
decode_pay_request_impl,
get_implementation_name,
get_ln_info_impl,
get_wallet_balance_impl,
@ -44,6 +52,10 @@ async def add_invoice(
return await add_invoice_impl(memo, value_msat, expiry, is_keysend)
async def decode_pay_request(pay_req: str) -> PaymentRequest:
return await decode_pay_request_impl(pay_req)
async def send_payment(
pay_req: str, timeout_seconds: int, fee_limit_msat: int
) -> Payment:

View file

@ -1,4 +1,4 @@
from app.models.lightning import Invoice, LnInfo, Payment
from app.models.lightning import Invoice, LnInfo, Payment, PaymentRequest
def get_implementation_name() -> str:
@ -15,6 +15,10 @@ async def add_invoice_impl(
raise NotImplementedError("c-lightning not yet implemented")
async def decode_pay_request_impl(pay_req: str) -> PaymentRequest:
raise NotImplementedError("c-lightning not yet implemented")
async def send_payment_impl(
pay_req: str, timeout_seconds: int, fee_limit_msat: int
) -> Payment:

View file

@ -9,6 +9,7 @@ from app.models.lightning import (
InvoiceState,
LnInfo,
Payment,
PaymentRequest,
WalletBalance,
invoice_from_grpc,
ln_info_from_grpc,
@ -62,6 +63,25 @@ async def add_invoice_impl(
return invoice
async def decode_pay_request_impl(pay_req: str) -> PaymentRequest:
try:
req = ln.PayReqString(pay_req=pay_req)
res = await lncfg.lnd_stub.DecodePayReq(req)
return PaymentRequest.from_grpc(res)
except grpc.aio._call.AioRpcError as error:
if (
error.details() != None
and error.details().find("checksum failed.") > -1
):
raise HTTPException(
status.HTTP_400_BAD_REQUEST, detail="Invalid payment request string"
)
else:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR, detail=error.details()
)
async def send_payment_impl(
pay_req: str, timeout_seconds: int, fee_limit_msat: int
) -> Payment:

View file

@ -4,17 +4,19 @@ from app.models.lightning import (
LightningStatus,
LnInfo,
Payment,
PaymentRequest,
WalletBalance,
)
from app.repositories.lightning import (
add_invoice,
decode_pay_request,
get_ln_info,
get_ln_status,
get_wallet_balance,
send_payment,
)
from app.routers.lightning_docs import send_payment_desc
from fastapi import APIRouter, HTTPException, status
from fastapi import APIRouter, HTTPException, Query, status
from fastapi.params import Depends
router = APIRouter(prefix="/lightning", tags=["Lightning"])
@ -107,3 +109,17 @@ async def get_info():
raise HTTPException(r.status_code, detail=r.detail)
except NotImplementedError as r:
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED, detail=r.args[0])
@router.get(
"/decode-pay-req",
name="lightning.decode-pay-req",
summary="DecodePayReq takes an encoded payment request string and attempts to decode it, returning a full description of the conditions encoded within the payment request.",
response_model=PaymentRequest,
response_description="A fully decoded payment request or a HTTP status 400 if the payment request cannot be decoded.",
dependencies=[Depends(JWTBearer())],
)
async def get_decode_pay_request(
pay_req: str = Query(..., description="The payment request string to be decoded")
):
return await decode_pay_request(pay_req)