-
Notifications
You must be signed in to change notification settings - Fork 3
/
models.py
145 lines (129 loc) · 4.77 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import json
import time
from typing import Dict
import bolt11
from fastapi import Query, Request
from lnbits.core.services import pay_invoice
from lnbits.exceptions import PaymentError
from loguru import logger
from pydantic import BaseModel, validator
from .db import db
from .exchange_rates import exchange_rate_providers, fiat_currencies
from .helpers import LnurlValidationError, get_callback_url
class CreateBleskomat(BaseModel):
name: str = Query(...)
fiat_currency: str = Query(...)
exchange_rate_provider: str = Query(...)
fee: str = Query(...)
@validator("fiat_currency")
def allowed_fiat_currencies(cls, v):
if v not in fiat_currencies.keys():
raise ValueError("Not allowed currency")
return v
@validator("exchange_rate_provider")
def allowed_providers(cls, v):
if v not in exchange_rate_providers.keys():
raise ValueError("Not allowed provider")
return v
@validator("fee")
def fee_type(cls, v):
if not isinstance(v, (str, float, int)):
raise ValueError("Fee type not allowed")
return v
class Bleskomat(BaseModel):
id: str
wallet: str
api_key_id: str
api_key_secret: str
api_key_encoding: str
name: str
fiat_currency: str
exchange_rate_provider: str
fee: str
class BleskomatLnurl(BaseModel):
id: str
bleskomat: str
wallet: str
hash: str
tag: str
params: str
api_key_id: str
initial_uses: int
remaining_uses: int
created_time: int
updated_time: int
def has_uses_remaining(self) -> bool:
# When initial uses is 0 then the LNURL has unlimited uses.
return self.initial_uses == 0 or self.remaining_uses > 0
def get_info_response_object(self, secret: str, req: Request) -> Dict[str, str]:
tag = self.tag
params = json.loads(self.params)
response = {"tag": tag}
if tag == "withdrawRequest":
for key in ["minWithdrawable", "maxWithdrawable", "defaultDescription"]:
response[key] = params[key]
response["callback"] = get_callback_url(req)
response["k1"] = secret
return response
def validate_action(self, query) -> None:
tag = self.tag
params = json.loads(self.params)
# Perform tag-specific checks.
if tag == "withdrawRequest":
for field in ["pr"]:
if field not in query:
raise LnurlValidationError(f'Missing required parameter: "{field}"')
# Check the bolt11 invoice(s) provided.
pr = query["pr"]
if "," in pr:
raise LnurlValidationError("Multiple payment requests not supported")
try:
invoice = bolt11.decode(pr)
except ValueError as exc:
raise LnurlValidationError(
'Invalid parameter ("pr"): Lightning payment request expected'
) from exc
if invoice.amount_msat < params["minWithdrawable"]:
raise LnurlValidationError(
"Amount in invoice must be greater "
'than or equal to "minWithdrawable"'
)
if invoice.amount_msat > params["maxWithdrawable"]:
raise LnurlValidationError(
'Amount in invoice must be less than or equal to "maxWithdrawable"'
)
else:
raise LnurlValidationError(f'Unknown subprotocol: "{tag}"')
async def execute_action(self, query):
self.validate_action(query)
used = False
async with db.connect() as conn:
if self.initial_uses > 0:
used = await self.use(conn)
if not used:
raise LnurlValidationError("Maximum number of uses already reached")
tag = self.tag
if tag == "withdrawRequest":
try:
await pay_invoice(
wallet_id=self.wallet, payment_request=query["pr"]
)
except (ValueError, PermissionError, PaymentError) as exc:
raise LnurlValidationError(
"Failed to pay invoice: " + str(exc)
) from exc
except Exception as exc:
logger.error(str(exc))
raise LnurlValidationError("Unexpected error") from exc
async def use(self, conn) -> bool:
now = int(time.time())
result = await conn.execute(
"""
UPDATE bleskomat.bleskomat_lnurls
SET remaining_uses = remaining_uses - 1, updated_time = ?
WHERE id = ?
AND remaining_uses > 0
""",
(now, self.id),
)
return result.rowcount > 0