Ran black on bleskomat

This commit is contained in:
benarc 2021-03-17 19:54:17 +00:00
parent c86bd74506
commit 20ef9f92a1
8 changed files with 70 additions and 75 deletions

View File

@ -6,19 +6,30 @@ from . import db
from .models import Bleskomat, BleskomatLnurl
from .helpers import generate_bleskomat_lnurl_hash
async def create_bleskomat(
*, wallet_id: str, name: str, fiat_currency: str, exchange_rate_provider: str, fee: str
) -> Bleskomat:
bleskomat_id = uuid4().hex
api_key_id = secrets.token_hex(8)
api_key_secret = secrets.token_hex(32)
api_key_encoding = "hex";
api_key_encoding = "hex"
await db.execute(
"""
INSERT INTO bleskomats (id, wallet, api_key_id, api_key_secret, api_key_encoding, name, fiat_currency, exchange_rate_provider, fee)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(bleskomat_id, wallet_id, api_key_id, api_key_secret, api_key_encoding, name, fiat_currency, exchange_rate_provider, fee),
(
bleskomat_id,
wallet_id,
api_key_id,
api_key_secret,
api_key_encoding,
name,
fiat_currency,
exchange_rate_provider,
fee,
),
)
bleskomat = await get_bleskomat(bleskomat_id)
assert bleskomat, "Newly created bleskomat couldn't be retrieved"
@ -65,7 +76,19 @@ async def create_bleskomat_lnurl(
INSERT INTO bleskomat_lnurls (id, bleskomat, wallet, hash, tag, params, api_key_id, initial_uses, remaining_uses, created_time, updated_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(bleskomat_lnurl_id, bleskomat.id, bleskomat.wallet, hash, tag, params, bleskomat.api_key_id, uses, uses, now, now),
(
bleskomat_lnurl_id,
bleskomat.id,
bleskomat.wallet,
hash,
tag,
params,
bleskomat.api_key_id,
uses,
uses,
now,
now,
),
)
bleskomat_lnurl = await get_bleskomat_lnurl(secret)
assert bleskomat_lnurl, "Newly created bleskomat LNURL couldn't be retrieved"

View File

@ -2,39 +2,41 @@ import httpx
import json
import os
fiat_currencies = json.load(open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'fiat_currencies.json'), 'r'))
fiat_currencies = json.load(
open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "fiat_currencies.json"), "r")
)
exchange_rate_providers = {
"bitfinex": {
"name": "Bitfinex",
"domain": "bitfinex.com",
"api_url": "https://api.bitfinex.com/v1/pubticker/{from}{to}",
"getter": lambda data, replacements: data["last_price"]
"getter": lambda data, replacements: data["last_price"],
},
"bitstamp": {
"name": "Bitstamp",
"domain": "bitstamp.net",
"api_url": "https://www.bitstamp.net/api/v2/ticker/{from}{to}/",
"getter": lambda data, replacements: data["last"]
"getter": lambda data, replacements: data["last"],
},
"coinbase": {
"name": "Coinbase",
"domain": "coinbase.com",
"api_url": "https://api.coinbase.com/v2/exchange-rates?currency={FROM}",
"getter": lambda data, replacements: data["data"]["rates"][replacements["TO"]]
"getter": lambda data, replacements: data["data"]["rates"][replacements["TO"]],
},
"coinmate": {
"name": "CoinMate",
"domain": "coinmate.io",
"api_url": "https://coinmate.io/api/ticker?currencyPair={FROM}_{TO}",
"getter": lambda data, replacements: data["data"]["last"]
"getter": lambda data, replacements: data["data"]["last"],
},
"kraken": {
"name": "Kraken",
"domain": "kraken.com",
"api_url": "https://api.kraken.com/0/public/Ticker?pair=XBT{TO}",
"getter": lambda data, replacements: data["result"]["XXBTZ" + replacements["TO"]]["c"][0]
}
"getter": lambda data, replacements: data["result"]["XXBTZ" + replacements["TO"]]["c"][0],
},
}
exchange_rate_providers_serializable = {}
@ -48,12 +50,7 @@ for ref, exchange_rate_provider in exchange_rate_providers.items():
async def fetch_fiat_exchange_rate(currency: str, provider: str):
replacements = {
"FROM" : "BTC",
"from" : "btc",
"TO" : currency.upper(),
"to" : currency.lower()
}
replacements = {"FROM": "BTC", "from": "btc", "TO": currency.upper(), "to": currency.lower()}
url = exchange_rate_providers[provider]["api_url"]
for key in replacements.keys():

View File

@ -21,11 +21,7 @@ def generate_bleskomat_lnurl_signature(payload: str, api_key_secret: str, api_ke
key = base64.b64decode(api_key_secret)
else:
key = bytes(f"{api_key_secret}")
return hmac.new(
key=key,
msg=payload.encode(),
digestmod=hashlib.sha256
).hexdigest()
return hmac.new(key=key, msg=payload.encode(), digestmod=hashlib.sha256).hexdigest()
def generate_bleskomat_lnurl_secret(api_key_id: str, signature: str):
@ -58,19 +54,21 @@ class LnurlValidationError(Exception):
def prepare_lnurl_params(tag: str, query: Dict[str, str]):
params = {}
if not is_supported_lnurl_subprotocol(tag):
raise LnurlValidationError(f"Unsupported subprotocol: \"{tag}\"")
raise LnurlValidationError(f'Unsupported subprotocol: "{tag}"')
if tag == "withdrawRequest":
params["minWithdrawable"] = float(query["minWithdrawable"])
params["maxWithdrawable"] = float(query["maxWithdrawable"])
params["defaultDescription"] = query["defaultDescription"]
if not params["minWithdrawable"] > 0:
raise LnurlValidationError("\"minWithdrawable\" must be greater than zero")
raise LnurlValidationError('"minWithdrawable" must be greater than zero')
if not params["maxWithdrawable"] >= params["minWithdrawable"]:
raise LnurlValidationError("\"maxWithdrawable\" must be greater than or equal to \"minWithdrawable\"")
raise LnurlValidationError('"maxWithdrawable" must be greater than or equal to "minWithdrawable"')
return params
encode_uri_component_safe_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()"
def query_to_signing_payload(query: Dict[str, str]) -> str:
# Sort the query by key, then stringify it to create the payload.
sorted_keys = sorted(query.keys(), key=str.lower)
@ -84,35 +82,17 @@ def query_to_signing_payload(query: Dict[str, str]) -> str:
unshorten_rules = {
"query": {
"n": "nonce",
"s": "signature",
"t": "tag"
},
"tags": {
"c": "channelRequest",
"l": "login",
"p": "payRequest",
"w": "withdrawRequest"
},
"query": {"n": "nonce", "s": "signature", "t": "tag"},
"tags": {"c": "channelRequest", "l": "login", "p": "payRequest", "w": "withdrawRequest"},
"params": {
"channelRequest": {
"pl": "localAmt",
"pp": "pushAmt"
},
"channelRequest": {"pl": "localAmt", "pp": "pushAmt"},
"login": {},
"payRequest": {
"pn": "minSendable",
"px": "maxSendable",
"pm": "metadata"
},
"withdrawRequest": {
"pn": "minWithdrawable",
"px": "maxWithdrawable",
"pd": "defaultDescription"
}
}
"payRequest": {"pn": "minSendable", "px": "maxSendable", "pm": "metadata"},
"withdrawRequest": {"pn": "minWithdrawable", "px": "maxWithdrawable", "pd": "defaultDescription"},
},
}
def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]:
new_query = {}
rules = unshorten_rules
@ -121,14 +101,14 @@ def unshorten_lnurl_query(query: Dict[str, str]) -> Dict[str, str]:
elif "t" in query:
tag = query["t"]
else:
raise LnurlValidationError("Missing required query parameter: \"tag\"")
raise LnurlValidationError('Missing required query parameter: "tag"')
# Unshorten tag:
if tag in rules["tags"]:
long_tag = rules["tags"][tag]
new_query["tag"] = long_tag
tag = long_tag
if not tag in rules["params"]:
raise LnurlValidationError(f"Unknown tag: \"{tag}\"")
raise LnurlValidationError(f'Unknown tag: "{tag}"')
for key in query:
if key in rules["params"][tag]:
short_param_key = key

View File

@ -47,7 +47,7 @@ async def api_bleskomat_lnurl():
# The API key ID, nonce, and tag should be present in the query string.
for field in ["id", "nonce", "tag"]:
if not field in query:
raise LnurlHttpError(f"Failed API key signature check: Missing \"{field}\"", HTTPStatus.BAD_REQUEST)
raise LnurlHttpError(f'Failed API key signature check: Missing "{field}"', HTTPStatus.BAD_REQUEST)
# URL signing scheme is described here:
# https://github.com/chill117/lnurl-node#how-to-implement-url-signing-scheme
@ -72,8 +72,7 @@ async def api_bleskomat_lnurl():
params = prepare_lnurl_params(tag, query)
if "f" in query:
rate = await fetch_fiat_exchange_rate(
currency=query["f"],
provider=bleskomat.exchange_rate_provider
currency=query["f"], provider=bleskomat.exchange_rate_provider
)
# Convert fee (%) to decimal:
fee = float(bleskomat.fee) / 100
@ -88,13 +87,7 @@ async def api_bleskomat_lnurl():
raise LnurlHttpError(e.message, HTTPStatus.BAD_REQUEST)
# Create a new LNURL using the query parameters provided in the signed URL.
params = json.JSONEncoder().encode(params)
lnurl = await create_bleskomat_lnurl(
bleskomat=bleskomat,
secret=secret,
tag=tag,
params=params,
uses=1
)
lnurl = await create_bleskomat_lnurl(bleskomat=bleskomat, secret=secret, tag=tag, params=params, uses=1)
# Reply with LNURL response object.
return jsonify(lnurl.get_info_response_object(secret)), HTTPStatus.OK

View File

@ -39,7 +39,7 @@ class BleskomatLnurl(NamedTuple):
def get_info_response_object(self, secret: str) -> Dict[str, str]:
tag = self.tag
params = json.loads(self.params)
response = { "tag": tag }
response = {"tag": tag}
if tag == "withdrawRequest":
for key in ["minWithdrawable", "maxWithdrawable", "defaultDescription"]:
response[key] = params[key]
@ -54,7 +54,7 @@ class BleskomatLnurl(NamedTuple):
if tag == "withdrawRequest":
for field in ["pr"]:
if not field in query:
raise LnurlValidationError(f"Missing required parameter: \"{field}\"")
raise LnurlValidationError(f'Missing required parameter: "{field}"')
# Check the bolt11 invoice(s) provided.
pr = query["pr"]
if "," in pr:
@ -62,13 +62,13 @@ class BleskomatLnurl(NamedTuple):
try:
invoice = bolt11.decode(pr)
except ValueError as e:
raise LnurlValidationError("Invalid parameter (\"pr\"): Lightning payment request expected")
raise LnurlValidationError('Invalid parameter ("pr"): Lightning payment request expected')
if invoice.amount_msat < params["minWithdrawable"]:
raise LnurlValidationError("Amount in invoice must be greater than or equal to \"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\"")
raise LnurlValidationError('Amount in invoice must be less than or equal to "maxWithdrawable"')
else:
raise LnurlValidationError(f"Unknown subprotocol: \"{tag}\"")
raise LnurlValidationError(f'Unknown subprotocol: "{tag}"')
async def execute_action(self, query: Dict[str, str]):
self.validate_action(query)
@ -105,6 +105,6 @@ class BleskomatLnurl(NamedTuple):
WHERE id = ?
AND remaining_uses > 0
""",
(now, self.id)
(now, self.id),
)
return result.rowcount > 0

View File

@ -7,6 +7,7 @@ from . import bleskomat_ext
from .exchange_rates import exchange_rate_providers_serializable, fiat_currencies
from .helpers import get_callback_url
@bleskomat_ext.route("/")
@validate_uuids(["usr"], required=True)
@check_user_exists()
@ -14,6 +15,6 @@ async def index():
bleskomat_vars = {
"callback_url": get_callback_url(),
"exchange_rate_providers": exchange_rate_providers_serializable,
"fiat_currencies": fiat_currencies
"fiat_currencies": fiat_currencies,
}
return await render_template("bleskomat/index.html", user=g.user, bleskomat_vars=bleskomat_vars)

View File

@ -58,13 +58,13 @@ async def api_bleskomat_create_or_update(bleskomat_id=None):
try:
fiat_currency = g.data["fiat_currency"]
exchange_rate_provider = g.data["exchange_rate_provider"]
rate = await fetch_fiat_exchange_rate(
currency=fiat_currency,
provider=exchange_rate_provider
)
rate = await fetch_fiat_exchange_rate(currency=fiat_currency, provider=exchange_rate_provider)
except Exception as e:
print(e)
return jsonify({"message": f"Failed to fetch BTC/{fiat_currency} currency pair from \"{exchange_rate_provider}\""}), HTTPStatus.INTERNAL_SERVER_ERROR
return (
jsonify({"message": f'Failed to fetch BTC/{fiat_currency} currency pair from "{exchange_rate_provider}"'}),
HTTPStatus.INTERNAL_SERVER_ERROR,
)
if bleskomat_id:
bleskomat = await get_bleskomat(bleskomat_id)

View File

@ -124,4 +124,5 @@ async def create_hash_check(
async def get_hash_check(the_hash: str, lnurl_id: str) -> Optional[HashCheck]:
row = await db.fetchone("SELECT * FROM hash_check WHERE id = ?", (the_hash,))
return HashCheck.from_row(row) if row else None