diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..f70a443a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.py] +indent_size = 4 +indent_style = space diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..4cb673fd --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +FLASK_APP=lnbits +FLASK_ENV=development diff --git a/.gitignore b/.gitignore new file mode 100755 index 00000000..51fce8c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.DS_Store +._* + +__pycache__ +*.py[cod] +*$py.class +.mypy_cache +.vscode + +*.egg +*.egg-info +.coverage +.pytest_cache +htmlcov +Pipfile.lock diff --git a/DEV.md b/DEV.md new file mode 100644 index 00000000..56bc068d --- /dev/null +++ b/DEV.md @@ -0,0 +1,29 @@ +For developers +============== + +LNbits uses [Flask](http://flask.pocoo.org/). +Feel free to contribute to the project. + +Application dependencies +------------------------ +The application uses [Pipenv][pipenv] to manage Python packages. +While in development, you will need to install all dependencies: + + $ pipenv shell + $ pipenv install --dev + +Running the server +------------------ + + $ flask run + +There is an environment variable called `FLASK_ENV` that has to be set to `development` +if you want to run Flask in debug mode with autoreload + +Style guide +----------- +Tab size is 4 spaces. Maximum line length is 120. You should run `black` before commiting any change. + + $ black lnbits + +[pipenv]: https://docs.pipenv.org/#install-pipenv-today diff --git a/LNbits/__init__.py b/LNbits/__init__.py new file mode 100644 index 00000000..4f10cd84 --- /dev/null +++ b/LNbits/__init__.py @@ -0,0 +1,636 @@ +from flask import Flask, render_template +from flask import Flask, redirect +from flask import request +from flask import jsonify +from flask import Flask, g +from random import seed +from random import random +from flask import json +import re +import os +import sqlite3 +import base64 +import lnurl +import requests +import hashlib +import time +import json +import bech32 + +# DATABASE = 'database.db' + +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__) + + +DEFAULT_PATH = "database.sqlite3" + + +def db_connect(db_path=DEFAULT_PATH): + con = sqlite3.connect(db_path) + return con + + +def encrypt_string(hash_string): + sha_signature = hashlib.sha256(hash_string.encode()).hexdigest() + return sha_signature + + +@app.route("/") +def home(): + + return render_template("index.html") + + +@app.route("/deletewallet") +def deletewallet(): + + thewal = request.args.get("wal") + + con = db_connect() + cur = con.cursor() + print(thewal) + cur.execute("select * from wallets WHERE hash = '" + str(thewal) + "'") + rowss = cur.fetchall() + + if len(rowss) > 0: + + cur.close() + print(rowss) + + con = db_connect() + cur = con.cursor() + + cur.execute("UPDATE wallets SET user = '" + "del" + rowss[0][4] + "' WHERE hash = '" + rowss[0][0] + "'") + + con.commit() + cur.close() + + con = db_connect() + cur = con.cursor() + + cur.execute("UPDATE wallets SET adminkey = '" + "del" + rowss[0][5] + "' WHERE hash = '" + rowss[0][0] + "'") + + con.commit() + cur.close() + + con = db_connect() + cur = con.cursor() + + cur.execute("UPDATE wallets SET inkey = '" + "del" + rowss[0][6] + "' WHERE hash = '" + rowss[0][0] + "'") + + con.commit() + cur.close() + + con = db_connect() + cur = con.cursor() + print(thewal) + cur.execute("select * from wallets WHERE user = '" + rowss[0][4] + "'") + rowsss = cur.fetchall() + + if len(rowsss) > 0: + cur.close() + return render_template("deletewallet.html", theid=rowsss[0][4], thewal=rowsss[0][0]) + else: + return render_template("index.html") + + else: + 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_string(payment_hash)[0:20] + inkey = encrypt_string(adminkey)[0:20] + thewal = encrypt_string(inkey)[0:20] + theid = encrypt_string(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_string(theid) + inkey = encrypt_string(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") + theamt = request.args.get("amt") + thenme = request.args.get("nme") + + if not thewal: + return render_template("index.html") + else: + # Checks if the user exists in "accounts" + con = db_connect() + cur = con.cursor() + print(thewal) + cur.execute("select * from accounts WHERE userhash = '" + str(theid) + "'") + rows = cur.fetchall() + + if len(rows) > 0: + cur.close() + + # Yes, check the user has a wallet + con = db_connect() + cur = con.cursor() + print(thewal) + cur.execute("select * from wallets WHERE user = '" + str(theid) + "'") + rowss = cur.fetchall() + + if len(rowss) > 0: + cur.close() + + # Checks if the current wallet exists + con = db_connect() + cur = con.cursor() + print(thewal) + cur.execute("select * from wallets WHERE hash = '" + str(thewal) + "'") + rowsss = cur.fetchall() + + if len(rowsss) > 0: + cur.close() + walb = rowsss[0][1].split(",")[-1] + return render_template( + "wallet.html", + thearr=rowss, + len=len(rowss), + walnme=rowsss[0][3], + user=theid, + walbal=walb, + theid=theid, + thewal=thewal, + transactions=rowsss[0][2], + adminkey=rowsss[0][5], + inkey=rowsss[0][6], + ) + else: + cur.close() + + con = db_connect() + cur = con.cursor() + + adminkey = encrypt_string(thewal) + inkey = encrypt_string(adminkey) + + cur.execute( + "INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + + thewal + + "',',0','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) + "'") + rowss = cur.fetchall() + cur.close() + + return render_template( + "wallet.html", + thearr=rowss, + len=len(rowss), + walnme=thenme, + walbal="0", + theid=theid, + thewal=thewal, + adminkey=adminkey, + inkey=inkey, + ) + else: + cur.close() + + con = db_connect() + cur = con.cursor() + + adminkey = encrypt_string(theid) + inkey = encrypt_string(adminkey) + + cur.execute( + "INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + + thewal + + "',',0','0','" + + thenme + + "','" + + theid + + "','" + + adminkey + + "','" + + inkey + + "')" + ) + con.commit() + cur.close() + + return render_template( + "wallet.html", + len=len("1"), + walnme=thenme, + walbal="0", + theid=theid, + thewal=thewal, + adminkey=adminkey, + inkey=inkey, + ) + + else: + cur.close() + 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_string(theid) + inkey = encrypt_string(adminkey) + + cur.execute( + "INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + + thewal + + "',',0','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( + "wallet.html", + len=len("1"), + walnme=thenme, + walbal="0", + theid=theid, + thewal=thewal, + adminkey=adminkey, + inkey=inkey, + ) + + +# API requests +@app.route("/v1/invoices", methods=["GET", "POST"]) +def api_invoices(): + if request.headers["Content-Type"] == "application/json": + + postedjson = request.json + print(postedjson) + + if "value" in postedjson: + if postedjson["value"].isdigit() == True: + if "memo" in postedjson: + con = db_connect() + cur = con.cursor() + + cur.execute( + "select * from wallets WHERE inkey = '" + request.headers["Grpc-Metadata-macaroon"] + "'" + ) + rows = cur.fetchall() + + if len(rows) > 0: + cur.close() + + dataj = {"amt": postedjson["value"], "memo": postedjson["memo"]} + headers = {"Authorization": "Basic %s" % INVOICE_KEY} + r = requests.post(url=API_ENDPOINT + "/addinvoice", json=dataj, headers=headers) + + data = r.json() + + pay_req = data["pay_req"] + payment_hash = data["payment_hash"] + + con = db_connect() + cur = con.cursor() + + cur.execute( + "INSERT INTO apipayments (payhash, amount, wallet, paid, inkey, memo) VALUES ('" + + payment_hash + + "','" + + postedjson["value"] + + "','" + + rows[0][0] + + "','0','" + + request.headers["Grpc-Metadata-macaroon"] + + "','" + + postedjson["memo"] + + "')" + ) + con.commit() + cur.close() + + return jsonify({"pay_req": pay_req, "payment_hash": payment_hash}), 200 + + else: + return jsonify({"ERROR": "NO KEY"}), 200 + else: + return jsonify({"ERROR": "NO MEMO"}), 200 + else: + return jsonify({"ERROR": "VALUE MUST BE A NUMMBER"}), 200 + else: + return jsonify({"ERROR": "NO VALUE"}), 200 + else: + return jsonify({"ERROR": "MUST BE JSON"}), 200 + + +# API requests +@app.route("/v1/channels/transactions", methods=["GET", "POST"]) +def api_transactions(): + if request.headers["Content-Type"] == "application/json": + postedjson = request.json + print(postedjson) + print(postedjson["payment_request"]) + + if "payment_request" in postedjson: + con = db_connect() + cur = con.cursor() + print(request.headers["Grpc-Metadata-macaroon"]) + print() + cur.execute("select * from wallets WHERE adminkey = '" + request.headers["Grpc-Metadata-macaroon"] + "'") + rows = cur.fetchall() + if len(rows) > 0: + cur.close() + + 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 + + print(sats) + print(alpha) + print(num) + + dataj = {"invoice": postedjson["payment_request"]} + headers = {"Authorization": "Basic %s" % ADMIN_KEY} + r = requests.post(url=API_ENDPOINT + "/payinvoice", json=dataj, headers=headers) + data = r.json() + print(data) + + con = db_connect() + cur = con.cursor() + + cur.execute( + "INSERT INTO apipayments (payhash, amount, wallet, paid, adminkey, memo) VALUES ('" + + data["decoded"]["payment_hash"] + + "','" + + str(-int(data["decoded"]["num_satoshis"])) + + "','" + + rows[0][0] + + "','1','" + + request.headers["Grpc-Metadata-macaroon"] + + "','" + + data["decoded"]["description"] + + "')" + ) + con.commit() + cur.close() + + con = db_connect() + cur = con.cursor() + cur.execute("select * from apipayments WHERE payhash = '" + data["decoded"]["payment_hash"] + "'") + rowss = cur.fetchall() + cur.close() + + data["decoded"]["num_satoshis"] + + lastamt = rows[0][1].split(",") + newamt = int(lastamt[-1]) - int(data["decoded"]["num_satoshis"]) + updamt = rows[0][1] + "," + str(newamt) + thetime = time.time() + transactions = ( + rows[0][2] + "!" + rowss[0][5] + "," + str(thetime) + "," + str(rowss[0][1]) + "," + str(newamt) + ) + + con = db_connect() + cur = con.cursor() + + cur.execute( + "UPDATE wallets SET balance = '" + + updamt + + "', transactions = '" + + transactions + + "' WHERE hash = '" + + rows[0][0] + + "'" + ) + con.commit() + cur.close() + + return jsonify({"PAID": "TRUE"}), 200 + else: + return jsonify({"ERROR": "BAD AUTH"}), 200 + return jsonify({"ERROR": "NO PAY REQ"}), 200 + + return jsonify({"ERROR": "MUST BE JSON"}), 200 + + +@app.route("/v1/invoice/", 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") diff --git a/LNbits/__pycache__/lnurlreq.cpython-37.pyc b/LNbits/__pycache__/lnurlreq.cpython-37.pyc deleted file mode 100644 index 760e879b..00000000 Binary files a/LNbits/__pycache__/lnurlreq.cpython-37.pyc and /dev/null differ diff --git a/LNbits/requirements.txt b/LNbits/requirements.txt deleted file mode 100644 index cc1b216d..00000000 --- a/LNbits/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -lnurl==0.1.0 -Flask==1.1.1 -requests==2.21.0 diff --git a/LNbits/server.py b/LNbits/server.py deleted file mode 100644 index 5a115669..00000000 --- a/LNbits/server.py +++ /dev/null @@ -1,511 +0,0 @@ -from flask import Flask, render_template -from flask import Flask, redirect -from flask import request -from flask import jsonify -from flask import Flask, g -from random import seed -from random import random -from flask import json -import re -import os -import sqlite3 -import base64 -import lnurl -import requests -import hashlib -import time -import json -import bech32 - -#DATABASE = 'database.db' - -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__) - - -DEFAULT_PATH = "database.sqlite3" - - -def db_connect(db_path=DEFAULT_PATH): - con = sqlite3.connect(db_path) - return con - -def encrypt_string(hash_string): - sha_signature = \ - hashlib.sha256(hash_string.encode()).hexdigest() - return sha_signature - - -@app.route('/') -def home(): - - return render_template('index.html') - -@app.route('/deletewallet') -def deletewallet(): - - thewal = request.args.get('wal'); - - con = db_connect() - cur = con.cursor() - print(thewal) - cur.execute("select * from wallets WHERE hash = '" + str(thewal) + "'") - rowss = cur.fetchall() - - if len(rowss) > 0: - - cur.close() - print(rowss) - - con = db_connect() - cur = con.cursor() - - cur.execute("UPDATE wallets SET user = '" + "del" + rowss[0][4] + "' WHERE hash = '" + rowss[0][0] + "'") - - con.commit() - cur.close() - - con = db_connect() - cur = con.cursor() - - cur.execute("UPDATE wallets SET adminkey = '" + "del" + rowss[0][5] + "' WHERE hash = '" + rowss[0][0] + "'") - - con.commit() - cur.close() - - - con = db_connect() - cur = con.cursor() - - cur.execute("UPDATE wallets SET inkey = '" + "del" + rowss[0][6] + "' WHERE hash = '" + rowss[0][0] + "'") - - con.commit() - cur.close() - - con = db_connect() - cur = con.cursor() - print(thewal) - cur.execute("select * from wallets WHERE user = '" + rowss[0][4] + "'") - rowsss = cur.fetchall() - - if len(rowsss) > 0: - cur.close() - return render_template('deletewallet.html', theid = rowsss[0][4], thewal = rowsss[0][0]) - else: - return render_template('index.html') - - else: - 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_string(payment_hash)[0:20] - inkey = encrypt_string(adminkey)[0:20] - thewal = encrypt_string(inkey)[0:20] - theid = encrypt_string(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_string(theid) - inkey = encrypt_string(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'); - theamt = request.args.get('amt'); - thenme = request.args.get('nme'); - - if not thewal: - return render_template('index.html') - else: - #Checks if the user exists in "accounts" - con = db_connect() - cur = con.cursor() - print(thewal) - cur.execute("select * from accounts WHERE userhash = '" + str(theid) + "'") - rows = cur.fetchall() - - if len(rows) > 0: - cur.close() - - #Yes, check the user has a wallet - con = db_connect() - cur = con.cursor() - print(thewal) - cur.execute("select * from wallets WHERE user = '" + str(theid) + "'") - rowss = cur.fetchall() - - if len(rowss) > 0: - cur.close() - - #Checks if the current wallet exists - con = db_connect() - cur = con.cursor() - print(thewal) - cur.execute("select * from wallets WHERE hash = '" + str(thewal) + "'") - rowsss = cur.fetchall() - - if len(rowsss) > 0: - cur.close() - walb = rowsss[0][1].split(",")[-1] - return render_template('wallet.html', thearr = rowss, len = len(rowss), walnme = rowsss[0][3], user = theid, walbal = walb, theid = theid, thewal = thewal, transactions = rowsss[0][2], adminkey = rowsss[0][5], inkey = rowsss[0][6]) - else: - cur.close() - - con = db_connect() - cur = con.cursor() - - adminkey = encrypt_string(thewal) - inkey = encrypt_string(adminkey) - - cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0','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) + "'") - rowss = cur.fetchall() - cur.close() - - return render_template('wallet.html', thearr = rowss, len = len(rowss), walnme = thenme, walbal = '0', theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey) - else: - cur.close() - - con = db_connect() - cur = con.cursor() - - adminkey = encrypt_string(theid) - inkey = encrypt_string(adminkey) - - cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0','0','" + thenme + "','" + theid + "','" + adminkey + "','" + inkey + "')") - con.commit() - cur.close() - - return render_template('wallet.html', len = len("1"), walnme = thenme, walbal = '0', theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey) - - else: - cur.close() - 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_string(theid) - inkey = encrypt_string(adminkey) - - cur.execute("INSERT INTO wallets (hash, balance, transactions, name, user, adminkey, inkey) VALUES ('" + thewal + "',',0','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('wallet.html', len = len("1"), walnme = thenme, walbal = '0', theid = theid, thewal = thewal, adminkey = adminkey, inkey = inkey) - - - -#API requests -@app.route('/v1/invoices', methods=['GET', 'POST']) -def api_invoices(): - if request.headers['Content-Type'] == 'application/json': - - postedjson = request.json - print(postedjson) - - if "value" in postedjson: - if postedjson["value"].isdigit() == True: - if "memo" in postedjson: - con = db_connect() - cur = con.cursor() - - cur.execute("select * from wallets WHERE inkey = '" + request.headers['Grpc-Metadata-macaroon']+ "'") - rows = cur.fetchall() - - if len(rows) > 0: - cur.close() - - dataj = {'amt': postedjson["value"], 'memo': postedjson["memo"]} - headers = {'Authorization': 'Basic %s' % INVOICE_KEY} - r = requests.post(url = API_ENDPOINT + "/addinvoice", json = dataj, headers = headers) - - data = r.json() - - pay_req = data['pay_req'] - payment_hash = data['payment_hash'] - - con = db_connect() - cur = con.cursor() - - cur.execute("INSERT INTO apipayments (payhash, amount, wallet, paid, inkey, memo) VALUES ('" + payment_hash + "','" + postedjson["value"] + "','" + rows[0][0] + "','0','" + request.headers['Grpc-Metadata-macaroon'] + "','" + postedjson["memo"] + "')") - con.commit() - cur.close() - - return jsonify({"pay_req": pay_req, "payment_hash": payment_hash}), 200 - - else: - return jsonify({"ERROR": "NO KEY"}), 200 - else: - return jsonify({"ERROR": "NO MEMO"}), 200 - else: - return jsonify({"ERROR": "VALUE MUST BE A NUMMBER"}), 200 - else: - return jsonify({"ERROR": "NO VALUE"}), 200 - else: - return jsonify({"ERROR": "MUST BE JSON"}), 200 - - -#API requests -@app.route('/v1/channels/transactions', methods=['GET', 'POST']) -def api_transactions(): - if request.headers['Content-Type'] == 'application/json': - postedjson = request.json - print(postedjson) - print(postedjson["payment_request"]) - - if "payment_request" in postedjson: - con = db_connect() - cur = con.cursor() - print(request.headers['Grpc-Metadata-macaroon']) - print() - cur.execute("select * from wallets WHERE adminkey = '" + request.headers['Grpc-Metadata-macaroon']+ "'") - rows = cur.fetchall() - if len(rows) > 0: - cur.close() - - - - 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 - - print(sats) - print(alpha) - print(num) - - dataj = {'invoice': postedjson["payment_request"]} - headers = {'Authorization': 'Basic %s' % ADMIN_KEY} - r = requests.post(url = API_ENDPOINT + "/payinvoice", json = dataj, headers = headers) - data = r.json() - print(data); - - - con = db_connect() - cur = con.cursor() - - cur.execute("INSERT INTO apipayments (payhash, amount, wallet, paid, adminkey, memo) VALUES ('" + data["decoded"]["payment_hash"] + "','" + str(-int(data["decoded"]["num_satoshis"])) + "','" + rows[0][0] + "','1','" + request.headers['Grpc-Metadata-macaroon'] + "','" + data["decoded"]["description"] + "')") - con.commit() - cur.close() - - - con = db_connect() - cur = con.cursor() - cur.execute("select * from apipayments WHERE payhash = '" + data["decoded"]["payment_hash"] + "'") - rowss = cur.fetchall() - cur.close() - - - - data["decoded"]["num_satoshis"] - - - lastamt = rows[0][1].split(",") - newamt = int(lastamt[-1]) - int(data["decoded"]["num_satoshis"]) - updamt = rows[0][1] + "," + str(newamt) - thetime = time.time() - transactions = rows[0][2] + "!" + rowss[0][5] + "," + str(thetime) + "," + str(rowss[0][1]) + "," + str(newamt) - - con = db_connect() - cur = con.cursor() - - - cur.execute("UPDATE wallets SET balance = '" + updamt + "', transactions = '" + transactions + "' WHERE hash = '" + rows[0][0] + "'") - con.commit() - cur.close() - - - - return jsonify({"PAID": "TRUE"}), 200 - else: - return jsonify({"ERROR": "BAD AUTH"}), 200 - return jsonify({"ERROR": "NO PAY REQ"}), 200 - - return jsonify({"ERROR": "MUST BE JSON"}), 200 - - - -@app.route('/v1/invoice/', 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') \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000..a83d4218 --- /dev/null +++ b/Pipfile @@ -0,0 +1,20 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[requires] +python_version = "3.7" + +[packages] +lnurl = "*" +flask = "*" +requests = "*" + +[dev-packages] +black = "==19.10b0" +flake8 = "*" +flake8-mypy = "*" +pytest = "*" +pytest-cov = "*" +pytest-sugar = "*" diff --git a/LNbits/database.sqlite3 b/database.sqlite3 similarity index 100% rename from LNbits/database.sqlite3 rename to database.sqlite3 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..55ec8d78 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +line-length = 120 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..2a2a986e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +bech32==1.1.0 +certifi==2019.11.28 +chardet==3.0.4 +click==7.0 +flask==1.1.1 +idna==2.8 +itsdangerous==1.1.0 +jinja2==2.10.3 +lnurl==0.1.1 +markupsafe==1.1.1 +requests==2.22.0 +urllib3==1.25.7 +werkzeug==0.16.0 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b