commit dc844c79c2125d557b42c59748bae9bc998cc574
parent 4eb3da7a2241342647ef77da6d0f4dfe7b27e57c
Author: Nick <nick@nickfarrow.com>
Date:   Mon, 28 Mar 2022 17:09:55 +1100
Add lightning node info page (#57)
* Add lightning node info page
* pr feedback
Diffstat:
9 files changed, 133 insertions(+), 16 deletions(-)
diff --git a/config.py b/config.py
@@ -62,6 +62,7 @@ tunnel_host = get_opt("tunnel_host", None)
 tunnel_port = get_opt("tunnel_port", 22)
 tor_proxy = get_opt("tor_proxy", None)
 onchain_dust_limit = get_opt("onchain_dust_limit", 0.00000546)
+node_info = get_opt("node_info", None)
 pollrate = get_opt("pollrate", 15)
 payment_timeout = get_opt("payment_timeout", 60*60)
 required_confirmations = get_opt("required_confirmations", 2)
diff --git a/config.toml b/config.toml
@@ -13,6 +13,8 @@ rpcport = "8332"
 wallet = ""
 
 ## LND :: Add "lnd" to payment_methods
+# You can display your node connection so users can open channels with you by setting
+# node_info="uri" (manually) or true (fetch if macaroon has access to `getinfo`)
 [lnd]
 host = "127.0.0.1"
 lnd_dir = "~/.lnd/"
@@ -24,6 +26,8 @@ lnd_macaroon = "invoice.macaroon"
 ## CLIGHTNING :: Add "clightning" to payment_methods
 # If remote clightning, make sure `ssh -nNT -L {local_lightning-rpc}:{remote_lightning-rpc} {tunnel_host} -p {tunnel_port}`
 # creates a lightning-rpc unix domain socket. (use full paths local: /home/install/satsale/lightning-rpc)
+# You can display your node connection so users can open channels with you by setting
+# node_info="uri" (manually) or true (fetch if macaroon has access to `getinfo`)
 [clightning]
 clightning_rpc_file = "/home/user/.lightning/bitcoin/lightning-rpc"
 
@@ -54,6 +58,10 @@ api_key_path = "SatSale_API_key"
 # below this value.
 onchain_dust_limit = 0.00000546
 
+# You can display your node uri so users can open channels with you by setting
+# node_info="uri" (manually) or true (use `admin.macaroon` to fetch `getinfo`)
+#node_info = "uri"
+
 # Check for payment every xx seconds
 pollrate = 1
 
diff --git a/node/clightning.py b/node/clightning.py
@@ -72,6 +72,14 @@ class clightning:
         img.save("static/qr_codes/{}.png".format(uuid))
         return
 
+    def get_info(self):
+        return self.clightning.getinfo()
+
+    def get_uri(self):
+        info = self.get_info()
+        address = info["address"][0]
+        return info["id"] + "@" + address["address"] + ":" + str(address["port"])
+
     # Create lightning invoice
     def create_clightning_invoice(self, btc_amount, label):
         # Multiplying by 10^8 to convert to satoshi units
diff --git a/node/lnd.py b/node/lnd.py
@@ -48,7 +48,7 @@ class lnd:
                     logging.info(inv)
                 else:
                     logging.info("Getting lnd info...")
-                    info = self.lnd.get_info()
+                    info = self.get_info()
                     logging.info(info)
 
                 logging.info("Successfully contacted lnd.")
@@ -149,6 +149,13 @@ class lnd:
         logging.info(ret)
         return
 
+    def get_info(self):
+        return json.loads(MessageToJson(self.lnd.get_info()))
+
+    def get_uri(self):
+        info = self.get_info()
+        return info["uris"][0]
+
     # Check whether the payment has been paid
     def check_payment(self, rhash):
         invoice_status = json.loads(
diff --git a/satsale.py b/satsale.py
@@ -14,9 +14,11 @@ import uuid
 import sqlite3
 from pprint import pprint
 import json
+import qrcode
 import logging
 
 import config
+
 # Initialise logging before importing other modules
 logging.basicConfig(
     format="[%(asctime)s] [%(levelname)s] %(message)s",
@@ -61,6 +63,7 @@ database.migrate_database()
 def index():
     params = dict(request.args)
     params["currency"] = config.base_currency
+    params["node_info"] = config.node_info
     headers = {"Content-Type": "text/html"}
     return make_response(render_template("donate.html", params=params), 200, headers)
 
@@ -71,6 +74,7 @@ def pay():
     params = dict(request.args)
     params["payment_methods"] = enabled_payment_methods
     params["redirect"] = config.redirect
+    params["node_info"] = config.node_info
     # Render payment page with the request arguments (?amount= etc.)
     headers = {"Content-Type": "text/html"}
     return make_response(render_template("index.html", params=params), 200, headers)
@@ -152,8 +156,12 @@ class create_payment(Resource):
         if node.is_onchain and btc_value < config.onchain_dust_limit:
             logging.warning(
                 "Requested onchain payment for {} {} below dust limit ({} < {})".format(
-                    base_amount, currency, btc_amount_format(btc_value),
-                    btc_amount_format(config.onchain_dust_limit)))
+                    base_amount,
+                    currency,
+                    btc_amount_format(btc_value),
+                    btc_amount_format(config.onchain_dust_limit),
+                )
+            )
             return {"message": "Amount below dust limit."}, 406
 
         invoice = {
@@ -163,7 +171,7 @@ class create_payment(Resource):
             "method": payment_method,
             "time": time.time(),
             "webhook": webhook,
-            "onchain_dust_limit": config.onchain_dust_limit
+            "onchain_dust_limit": config.onchain_dust_limit,
         }
 
         # Get an address / invoice, and create a QR code
@@ -354,6 +362,27 @@ for method in config.payment_methods:
         logging.info("Connection to lightning node (clightning) successful.")
         enabled_payment_methods.append("lightning")
 
+# Add node connection page
+if config.node_info is not None:
+    @app.route("/node/")
+    def node():
+        if config.node_info == True:
+            uri = lightning_node.get_uri()
+        else:
+            uri = config.node_info
+        img = qrcode.make(uri)
+        img.save("static/qr_codes/node.png")
+        headers = {"Content-Type": "text/html"}
+        return make_response(
+            render_template("node.html", params={"uri": uri}), 200, headers
+        )
+
+# Add lightning address 
+if lightning_node.config['lightning_address'] is not None:
+    from gateways import lightning_address
+    lightning_address.add_ln_address_decorators(app, api, lightning_node)
+
+# Add Paynym
 if config.paynym is not None:
     paynym.insert_paynym_html(config.paynym)
 
diff --git a/static/style.css b/static/style.css
@@ -16,7 +16,7 @@ body {
     /* Rounded Corners */
     border-radius: 25px;
 
-    font-size: 16px
+    font-size: 16px;
 
     height: 275px;
     width:360px;
diff --git a/templates/donate.html b/templates/donate.html
@@ -28,7 +28,6 @@
     <meta property="twitter:description" content="Lightweight Bitcoin payment processor written in easily deployable Python. ">
     <meta property="twitter:image" content="https://user-images.githubusercontent.com/24557779/109666538-60ee4800-7bc3-11eb-8615-2cb1b239cc11.png">
 
-
 </head>
 
 
@@ -45,18 +44,29 @@
         </div>
 
         <div id="paymentForm">
-            <center>
-                <form id="pay" action='/pay' style="margin:0;padding0;">
-                    <h2 style="margin:0;padding0;">Amount:
+            <form id="pay" action='/pay'>
+                <div style="display:block;text-align: center;">
+                    <h2 style="margin:0;">Amount:
                     <input id="amountenter" style="display:inline" size="4" type="float" name="amount" id="amount" placeholder="{{ params.currency }}" required>
                     </h2>
                     <br>
                     <input class="button button1" style="width:40%" type="submit" value="Donate">
-                </form>
-            </center>
+                </div>
+            </form>
+            <div id="paybutton"></div>
+        </div>
+        <br>
+        <div id="row">
+            <div id="left">
+                {% if params.node_info %}
+                <small><a id="about" href="/node/" target="_blank">Open a Lightning channel with me!</a></small>
+                </br>
+                {% endif %}
+            </div>
+            <div id="right" style="text-align:right;">
+                <small style="vertical-align:middle"><a id="about" href="https://github.com/nickfarrow/SatSale" target="_blank">SatSale</a></small>   
+            </div>
         </div>
-
-        <div id="paybutton"></div>
     </div>
 </body>
 </html>
diff --git a/templates/index.html b/templates/index.html
@@ -25,7 +25,7 @@
     <div id="paybox">
         <div id="row" height="50px">
             <div id="left" style="display:inline-block;" height="75px">
-                <h1>Pay Bitcoin</h>
+                <h1>Pay Bitcoin</h1>
             </div>
             <div id="right">
                 <a id="qrClick" target="_blank"><img class="logo" id="qrImage" width="100px" src="{{ url_for('static', filename='logo.svg') }}"></a>
@@ -48,7 +48,7 @@
         </br>
 
         <div id="row">
-            <div id="left" style="text-align: left; padding: 0;">
+            <div id="left" style="width:40%; text-align: left; padding: 0;">
                 <div id="paymentMethodSwitchButton" style="display:none;">
                 {% if params.payment_methods|length > 1 %}
                     <select class="button" name="method" id="payment_method_select" onchange="replaceUrlParam(window.location, 'method', document.getElementById('payment_method_select').value);">
@@ -65,7 +65,11 @@
                 </div>
             </div>
 
-            <div id="right" style="text-align: right; padding: 10px 10px;">
+            <div id="right" style="width:60%; text-align: right; padding: 10px 10px;">
+                {% if params.node_info %}
+                    <small><a id="about" href="/node/" target="_blank">Open a channel with me!</a></small>
+                    </br>
+                {% endif %}
                 <small style="vertical-align:middle"><a id="about" href="https://github.com/nickfarrow/SatSale" target="_blank">SatSale</a></small>
             </div>
         </div>
diff --git a/templates/node.html b/templates/node.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <title>SatSale</title>
+    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+
+    <script src="{{ url_for('static', filename='jquery-3.6.0.min.js') }}"></script>
+    <script src="{{ url_for('static', filename='socket.io.min.js') }}"></script>
+    <script src="{{ url_for('static', filename='satsale.js') }}"></script>
+
+    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+</head>
+
+
+<body>
+    <div id="paybox">
+        <div id="row" height="50px">
+            <div id="left" style="display:inline-block;" height="75px">
+                <h1>Open a channel:</h1>
+            </div>
+            <div id="right">
+                <p><img class="qr" style="display:block; margin-left: auto; margin-right: auto;" width="100px" src="{{ url_for('static', filename='qr_codes/node.png') }}"></p>
+            </div>
+        </div>
+        <pre id="nodeInfo" style="padding:10px;background-color:black;color:white;border-radius:15px;white-space:pre-wrap;word-wrap:break-word;">
+            <span id="node_url"></span>
+        </pre>
+        
+        </br>
+
+        <div id="row">
+            <div id="left" style="text-align: left; padding: 0;">
+            </div>
+
+            <div id="right" style="text-align: right; padding: 10px 10px;">
+                <small style="vertical-align:middle"><a id="about" href="https://github.com/nickfarrow/SatSale" target="_blank">SatSale</a></small>
+            </div>
+        </div>
+    </div>
+
+    <script type="text/javascript">
+        node_info = {{ params|tojson }};
+        console.log(node_info);
+        document.getElementById("node_url").innerHTML = node_info.uri;
+
+    </script>
+
+</body>
+</html>