diff --git a/request-invoice/README.md b/request-invoice/README.md deleted file mode 100644 index dcf6347..0000000 --- a/request-invoice/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Http Invoice Request Server plugin - - -This plugin starts a minimal, rate limited HTTP Server and returns a invoice on the following GET request: - -``/invoice//`` - -``` -$ lightning-cli invoiceserver start -started server successfully on port 8089 - -$ curl http://localhost:8809/invoice/100000/some-text-here -{"bolt11":"lnbc100000n1p0yr9sxpp50t9rnvw5slcjn6m97fmujpp9xd6w0h26dpvajcv8xwhu42xk5vlqdq8vej8xcgxqyj...","expires_at":1581961350,"payment_hash":"7aca39b1d487f129eb65f277c904253374e7dd5a6859d9618733afcaa8d6a33e", ...} -``` - -The webserver is rate-limited, meaning that only a certain amount of request per minute is allowed (to prevent DoS attacks). - -The json invoice can be used by other services, for example a service for tipping, donations, PoS devices, ... - - -Once the plugin is active you can run `lightning-cli help invoiceserver` to -learn about the command line API: - -Commands: `start`/`stop`/`restart`/`status`. - -In the file `.lightning/bitcoin/.env` the `port` can be set (default 8089). - -##### Onion routing -Exposing the url using Onion routing is easy. Install tor and -add the service to `/etc/tor/torrc` -``` -HiddenServiceDir /home/bitcoin/tor/request-invoice-service_v2/ -HiddenServicePort 80 127.0.0.1:8809 - -``` -and restart tor to create the url -``` -$ systemctl stop tor && systemctl start tor -$ cat /home/bitcoin/tor/request-invoice-service_v2/hostname -fkfuvjrbj6cfqppq6xcgfwmp4p23wq2unzlnqmdi6ibqtg2aq7thp2qd.onion -``` - -Use tor browser to visit the url and create a invoice (and make a donation?). -http://fkfuvjrbj6cfqppq6xcgfwmp4p23wq2unzlnqmdi6ibqtg2aq7thp2qd.onion/invoice/1000/donation - - - diff --git a/request-invoice/requestinvoice.py b/request-invoice/requestinvoice.py deleted file mode 100755 index abb272b..0000000 --- a/request-invoice/requestinvoice.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 - -from flask import Flask -from flask_limiter import Limiter -from flask_limiter.util import get_remote_address -from pathlib import Path -from pyln.client import Plugin -from tornado.httpserver import HTTPServer -from tornado.ioloop import IOLoop -from tornado.wsgi import WSGIContainer - - -import asyncio -import os -import threading -import uuid - -plugin = Plugin() -app = Flask(__name__) -limiter = Limiter( - app, - key_func=get_remote_address, - default_limits=["2000 per day", "20 per minute"] -) - -jobs = {} - - - -@limiter.limit("20 per minute") -@app.route('/invoice//') -def getinvoice(amount, description): - global plugin - label = "ln-getinvoice-{}".format(uuid.uuid4()) - invoice = plugin.rpc.invoice(int(amount)*1000, label, description) - return invoice - -def worker(port): - asyncio.set_event_loop(asyncio.new_event_loop()) - - print('Starting server on port {port}'.format( - port=port - )) - app.config['SECRET_KEY'] = os.getenv( - "REQUEST_INVOICE_SECRET", - default=uuid.uuid4()) - - http_server = HTTPServer(WSGIContainer(app)) - http_server.listen(port) - IOLoop.instance().start() - - -def start_server(port): - if port in jobs: - raise ValueError("server already running on port {port}".format(port=port)) - - p = threading.Thread( - target=worker, args=(port,), daemon=True) - - jobs[port] = p - p.start() - - -def stop_server(port): - if port in jobs: - jobs[port].terminate() - del jobs[port] - else: - raise ValueError("No server listening on port {port}".format(port=port)) - - -@plugin.method('invoiceserver') -def invoiceserver(request, command="start"): - """Starts a server for requestiong invoices. - - A rate limited HTTP Server returns a invoice on the following GET request: - /invoice// - where amount is in Satoshis. - The plugin takes one of the following commands: - {start/stop/status/restart}. - """ - commands = {"start", "stop", "status","restart"} - port = os.getenv("FLASKPORT", default = 8809) - - # if command unknown make start our default command - if command not in commands: - command = "start" - - if command == "start": - try: - start_server(port) - return "Invoice server started successfully on port {}".format(port) - except Exception as e: - return "Error starting server on port {port}: {e}".format( - port=port, e=e - ) - - if command == "stop": - try: - stop_server(port) - return "Invoice server stopped on port {}".format(port) - except Exception as e: - return "Could not stop server on port {port}: {e}".format( - port=port, e=e - ) - - if command == "status": - if port in jobs: - return "Invoice server active on port {}".format(port) - else: - return "Invoice server not active." - - if command == "restart": - stop_server(port) - start_server(port) - return "Invoice server restarted" - - -@plugin.init() -def init(options, configuration, plugin): - port = os.getenv("REQUEST_INVOICE_PORT", default = 8809) - start_server(port) - - -plugin.run() diff --git a/request-invoice/requirements.txt b/request-invoice/requirements.txt deleted file mode 100644 index a0da9a2..0000000 --- a/request-invoice/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -python-dotenv>=0.13.0 -flask==1.1.4 -flask-limiter -tornado -pyln-client -requests diff --git a/request-invoice/test_request-invoice.py b/request-invoice/test_request-invoice.py deleted file mode 100644 index d639ae8..0000000 --- a/request-invoice/test_request-invoice.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -from pyln.testing.fixtures import * # noqa: F401,F403 -import requests -from subprocess import check_output - -plugin_path = os.path.join(os.path.dirname(__file__), "requestinvoice.py") - - -def test_requestinvoice_starts(node_factory): - l1 = node_factory.get_node() - # Test dynamically - l1.rpc.plugin_start(plugin_path) - l1.rpc.plugin_stop(plugin_path) - l1.rpc.plugin_start(plugin_path) - l1.stop() - # Then statically - l1.daemon.opts["plugin"] = plugin_path - l1.start() - - l1.daemon.logsearch_start = 0 - l1.daemon.wait_for_log(r'Starting server on port 8809') - - r = requests.get('http://localhost:8809/invoice/1000/text') - - # check for hanging process if status_code = 500 - assert(r.status_code == 200) - - # returned valid invoice? - b = r.json() - assert(b['bolt11'][:8] == "lnbcrt10") - - - # test rate-limit - for i in range(0,20): - r = requests.get('http://localhost:8809/invoice/1000/text') - assert(r.status_code == 429) - assert("429 Too Many Requests" in r.text )