lnbits/LNbits/__init__.py
2019-12-13 23:02:56 +01:00

454 lines
14 KiB
Python

import lnurl
import requests
import time
from flask import Flask, jsonify, render_template, request
from .db import Database, DEFAULT_PATH
from .helpers import encrypt
INVOICE_KEY = "YOUR-LNTXBOT-INVOICE-KEY" # In the lntxbot bot on telegram type "/api"
ADMIN_KEY = "YOUR-LNTXBOT-ADMIN-KEY"
API_ENDPOINT = "YOUR-LNTXBOT-API-BASE-URL"
app = Flask(__name__)
def db_connect(db_path=DEFAULT_PATH):
import sqlite3
con = sqlite3.connect(db_path)
return con
@app.route("/")
def home():
return render_template("index.html")
@app.route("/deletewallet")
def deletewallet():
thewal = request.args.get("wal")
with Database() as db:
wallets = db.fetchall("SELECT * FROM wallets WHERE hash = ?", (thewal,))
if wallets:
db.execute("UPDATE wallets SET user = ? WHERE hash = ?", (f"del{wallets[0][4]}", wallets[0][0]))
db.execute("UPDATE wallets SET adminkey = ? WHERE hash = ?", (f"del{wallets[0][5]}", wallets[0][0]))
db.execute("UPDATE wallets SET inkey = ? WHERE hash = ?", (f"del{wallets[0][6]}", wallets[0][0]))
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (wallets[0][4],))
if user_wallets:
return render_template("deletewallet.html", theid=user_wallets[0][4], thewal=user_wallets[0][0])
return render_template("index.html")
@app.route("/lnurlwallet")
def lnurlwallet():
# put in a function
thestr = request.args.get("lightning")
lnurll = lnurl.decode(thestr)
r = requests.get(url=lnurll)
data = r.json()
callback = data["callback"]
maxwithdraw = data["maxWithdrawable"]
withdraw = int(maxwithdraw / 1000)
k1 = data["k1"]
# get invoice
dataj = {"amt": str(withdraw)}
headers = {"Authorization": "Basic %s" % INVOICE_KEY}
rr = requests.post(url=API_ENDPOINT + "/addinvoice", json=dataj, headers=headers)
dataa = rr.json()
# get callback
pay_req = dataa["pay_req"]
payment_hash = dataa["payment_hash"]
invurl = callback + "&k1=" + k1 + "&pr=" + pay_req
rrr = requests.get(url=invurl)
dataaa = rrr.json()
print(dataaa)
print("poo")
if dataaa["status"] == "OK":
data = ""
while data == "":
r = requests.post(url=API_ENDPOINT + "/invoicestatus/" + str(payment_hash), headers=headers)
data = r.json()
print(r.json())
adminkey = encrypt(payment_hash)[0:20]
inkey = encrypt(adminkey)[0:20]
thewal = encrypt(inkey)[0:20]
theid = encrypt(thewal)[0:20]
thenme = "Bitcoin LN Wallet"
con = db_connect()
cur = con.cursor()
cur.execute("INSERT INTO accounts (userhash) VALUES ('" + theid + "')")
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
adminkey = encrypt(theid)
inkey = encrypt(adminkey)
cur.execute(
"INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('"
+ thewal
+ "',',0,"
+ str(withdraw)
+ "','0','"
+ thenme
+ "','"
+ theid
+ "','"
+ adminkey
+ "','"
+ inkey
+ "')"
)
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
print(thewal)
cur.execute("select * from wallets WHERE user = '" + str(theid) + "'")
rows = cur.fetchall()
con.commit()
cur.close()
return render_template(
"lnurlwallet.html",
len=len("1"),
walnme=thenme,
walbal=str(withdraw),
theid=theid,
thewal=thewal,
adminkey=adminkey,
inkey=inkey,
)
else:
return render_template("index.html")
@app.route("/wallet")
def wallet():
theid = request.args.get("usr")
thewal = request.args.get("wal")
thenme = request.args.get("nme")
if not thewal:
return render_template("index.html")
with Database() as db:
user_exists = len(db.fetchall("SELECT * FROM accounts WHERE userhash = ?", (theid,))) > 0
# user exists
# -----------
if user_exists:
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (theid,))
# user has wallets
# ----------------
if user_wallets:
wallet = db.fetchall("SELECT * FROM wallets WHERE hash = ?", (thewal,))
if wallet:
walb = str(wallet[0][1]).split(",")[-1]
return render_template(
"wallet.html",
thearr=user_wallets,
len=len(user_wallets),
walnme=wallet[0][3],
user=theid,
walbal=walb,
theid=theid,
thewal=thewal,
transactions=wallet[0][2],
adminkey=wallet[0][5],
inkey=wallet[0][6],
)
adminkey = encrypt(thewal)
inkey = encrypt(adminkey)
db.execute(
"INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) "
"VALUES (?, 0, 0, ?, ?, ?, ?)",
(thewal, thenme, theid, adminkey, inkey),
)
rows = db.fetchall("SELECT * FROM wallets WHERE user = ?", (theid,))
return render_template(
"wallet.html",
thearr=rows,
len=len(rows),
walnme=thenme,
walbal="0",
theid=theid,
thewal=thewal,
adminkey=adminkey,
inkey=inkey,
)
# user has no wallets
# -------------------
adminkey = encrypt(theid)
inkey = encrypt(adminkey)
db.execute(
"INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) "
"VALUES (?, 0, 0, ?, ?, ?, ?)",
(thewal, thenme, theid, adminkey, inkey),
)
return render_template(
"wallet.html",
len=len("1"),
walnme=thenme,
walbal="0",
theid=theid,
thewal=thewal,
adminkey=adminkey,
inkey=inkey,
)
# user does not exist: create an account
# --------------------------------------
db.execute("INSERT INTO accounts (userhash) VALUES (?)", (theid,))
adminkey = encrypt(theid)
inkey = encrypt(adminkey)
db.execute(
"INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) "
"VALUES (?, 0, 0, ?, ?, ?, ?)",
(thewal, thenme, theid, adminkey, inkey),
)
return render_template(
"wallet.html",
len=len("1"),
walnme=thenme,
walbal="0",
theid=theid,
thewal=thewal,
adminkey=adminkey,
inkey=inkey,
)
@app.route("/v1/invoices", methods=["GET", "POST"])
def api_invoices():
if request.headers["Content-Type"] != "application/json":
return jsonify({"ERROR": "MUST BE JSON"}), 200
postedjson = request.json
if "value" not in postedjson:
return jsonify({"ERROR": "NO VALUE"}), 200
if not postedjson["value"].isdigit():
return jsonify({"ERROR": "VALUE MUST BE A NUMMBER"}), 200
if "memo" not in postedjson:
return jsonify({"ERROR": "NO MEMO"}), 200
with Database() as db:
rows = db.fetchall("SELECT * FROM wallets WHERE inkey = ?", (request.headers["Grpc-Metadata-macaroon"],))
if not rows:
return jsonify({"ERROR": "NO KEY"}), 200
dataj = {"amt": postedjson["value"], "memo": postedjson["memo"]}
headers = {"Authorization": f"Basic {INVOICE_KEY}"}
r = requests.post(url=f"{API_ENDPOINT}/addinvoice", json=dataj, headers=headers)
data = r.json()
pay_req = data["pay_req"]
payment_hash = data["payment_hash"]
db.execute(
"INSERT INTO apipayments (payhash, amount, wallet, paid, inkey, memo) VALUES (?, ?, ?, 0, ?, ?)",
(
payment_hash,
postedjson["value"],
rows[0][0],
request.headers["Grpc-Metadata-macaroon"],
postedjson["memo"],
),
)
return jsonify({"pay_req": pay_req, "payment_hash": payment_hash}), 200
@app.route("/v1/channels/transactions", methods=["GET", "POST"])
def api_transactions():
if request.headers["Content-Type"] != "application/json":
return jsonify({"ERROR": "MUST BE JSON"}), 200
postedjson = request.json
if "payment_request" not in postedjson:
return jsonify({"ERROR": "NO PAY REQ"}), 200
with Database() as db:
wallets = db.fetchall("SELECT * FROM wallets WHERE adminkey = ?", (request.headers["Grpc-Metadata-macaroon"],))
if not wallets:
return jsonify({"ERROR": "BAD AUTH"}), 200
# TODO: check this unused code
# move sats calculation to a helper
# ---------------------------------
"""
s = postedjson["payment_request"]
result = re.search("lnbc(.*)1p", s)
tempp = result.group(1)
alpha = ""
num = ""
for i in range(len(tempp)):
if tempp[i].isdigit():
num = num + tempp[i]
else:
alpha += tempp[i]
sats = ""
if alpha == "n":
sats = int(num) / 10
elif alpha == "u":
sats = int(num) * 100
elif alpha == "m":
sats = int(num) * 100000
"""
# ---------------------------------
dataj = {"invoice": postedjson["payment_request"]}
headers = {"Authorization": f"Basic {ADMIN_KEY}"}
r = requests.post(url=f"{API_ENDPOINT}/payinvoice", json=dataj, headers=headers)
data = r.json()
db.execute(
"INSERT INTO apipayments (payhash, amount, wallet, paid, adminkey, memo) VALUES (?, ?, ?, 1, ?, ?)'",
(
data["decoded"]["payment_hash"],
str(-int(data["decoded"]["num_satoshis"])),
wallets[0][0],
request.headers["Grpc-Metadata-macaroon"],
data["decoded"]["description"],
),
)
payment = db.fetchall("SELECT * FROM apipayments WHERE payhash = ?", (data["decoded"]["payment_hash"],))[0]
lastamt = str(wallets[0][1]).split(",")
newamt = int(lastamt[-1]) - int(data["decoded"]["num_satoshis"])
updamt = wallets[0][1] + "," + str(newamt)
transactions = f"{wallets[0][2]}!{payment[5]},{time.time()},{payment[1]},{newamt}"
db.execute(
"UPDATE wallets SET balance = ?, transactions = ? WHERE hash = ?", (updamt, transactions, wallets[0][0])
)
return jsonify({"PAID": "TRUE"}), 200
@app.route("/v1/invoice/<payhash>", methods=["GET"])
def api_checkinvoice(payhash):
if request.headers["Content-Type"] == "application/json":
print(request.headers["Grpc-Metadata-macaroon"])
con = db_connect()
cur = con.cursor()
cur.execute("select * from apipayments WHERE payhash = '" + payhash + "'")
rows = cur.fetchall()
cur.close()
print(payhash)
if request.headers["Grpc-Metadata-macaroon"] == rows[0][4]:
if rows[0][3] == "0":
print(rows[0][3])
print("did it work?")
headers = {"Authorization": "Basic %s" % INVOICE_KEY}
r = requests.post(url=API_ENDPOINT + "/invoicestatus/" + payhash, headers=headers)
data = r.json()
print(r.json())
print("no")
if data == "":
return jsonify({"PAID": "FALSE"}), 400
else:
con = db_connect()
cur = con.cursor()
cur.execute("select * from wallets WHERE hash = '" + rows[0][2] + "'")
rowsss = cur.fetchall()
con.commit()
cur.close()
lastamt = rowsss[0][1].split(",")
newamt = int(lastamt[-1]) + int(rows[0][1])
updamt = rowsss[0][1] + "," + str(newamt)
thetime = time.time()
transactions = (
rowsss[0][2] + "!" + rows[0][5] + "," + str(thetime) + "," + str(rows[0][1]) + "," + str(newamt)
)
con = db_connect()
cur = con.cursor()
cur.execute(
"UPDATE wallets SET balance = '"
+ updamt
+ "', transactions = '"
+ transactions
+ "' WHERE hash = '"
+ rows[0][2]
+ "'"
)
con.commit()
cur.close()
con = db_connect()
cur = con.cursor()
cur.execute("UPDATE apipayments SET paid = '1' WHERE payhash = '" + payhash + "'")
con.commit()
cur.close()
return jsonify({"PAID": "TRUE"}), 200
else:
return jsonify({"PAID": "TRUE"}), 200
else:
return jsonify({"ERROR": "WRONG KEY"}), 400
else:
return jsonify({"ERROR": "NEEDS TO BE JSON"}), 400
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")