From 1f61280eef6374749aa9327fbfbbf8abfc8de338 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 11 Feb 2020 14:24:40 +0100 Subject: [PATCH] request-invoice init --- README.md | 2 + request-invoice/README.md | 34 +++++++ request-invoice/requestinvoice.py | 144 ++++++++++++++++++++++++++++++ request-invoice/requirements.txt | 4 + 4 files changed, 184 insertions(+) create mode 100644 request-invoice/README.md create mode 100644 request-invoice/requestinvoice.py create mode 100644 request-invoice/requirements.txt diff --git a/README.md b/README.md index def9668..01d42af 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Community curated plugins for c-lightning. | [pruning][pruning] | This plugin manages pruning of bitcoind such that it can always sync | | [rebalance][rebalance] | Keeps your channels balanced | | [reckless][reckless] | An **experimental** plugin manager (search/install plugins) | +| [requestinvoice][request-invoice] | Http server to request invoices | | [sauron][sauron] | A Bitcoin backend relying on [Esplora][esplora]'s API | | [sitzprobe][sitzprobe] | A Lightning Network payment rehearsal utility | | [sparko][sparko] | RPC over HTTP with fine-grained permissions, SSE and spark-wallet support | @@ -157,6 +158,7 @@ your environment. [js-api]: https://github.com/darosior/clightningjs [monitor]: https://github.com/renepickhardt/plugins/tree/master/monitor [reckless]: https://github.com/darosior/reckless +[request-invoice]: https://github.com/lightningd/plugins/tree/master/request-invoice [sauron]: https://github.com/lightningd/plugins/tree/master/sauron [zmq-home]: https://zeromq.org/ [zmq]: https://github.com/lightningd/plugins/tree/master/zmq diff --git a/request-invoice/README.md b/request-invoice/README.md new file mode 100644 index 0000000..0b69474 --- /dev/null +++ b/request-invoice/README.md @@ -0,0 +1,34 @@ + # Http Invoice Request Server plugin + + +This plugin starts a minimal, rate limited HTTP Server and returns a invoice on the following GET request: + +``` +/invoice// + +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, ... + + +Command line options are registered by the plugin and can be used to customize its behavior: + +| Command line option | Description | +|------------------------|---------------------------------------------------------------------| +| `--donation-autostart` | Should the donation server start automatically? (default: `true`) | +| `--donation-web-port` | Which port should the donation server listen to? (default: `8809`) | + + +Once the plugin is active you can run `lightning-cli help invoiceserver` to +learn about the command line API: + +Controls a invoice server with `start`/`stop`/`restart`/`list` on `port`. + + + + diff --git a/request-invoice/requestinvoice.py b/request-invoice/requestinvoice.py new file mode 100644 index 0000000..fb40cec --- /dev/null +++ b/request-invoice/requestinvoice.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 + +import multiprocessing + +from flask import Flask +from flask import jsonify +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address +from tornado.wsgi import WSGIContainer +from tornado.httpserver import HTTPServer +from tornado.ioloop import IOLoop + +from pyln.client import LightningRpc, Plugin +from time import time +from random import random + + +# change this +secret= 'caba27ba-45c7-4495-aa53-fd6a5866fbd8' + + +plugin = Plugin() +app = Flask(__name__) +limiter = Limiter( + app, + key_func=get_remote_address, + default_limits=["200 per day", "50 per hour"] +) + + +@limiter.limit("20 per minute") +def getinvoice(amount, description): + global plugin + label = "ln-getinvoice-{}".format(random()) + invoice = plugin.rpc.invoice(int(amount)*1000, label, description) + return invoice + + +def worker(port): + app.config['SECRET_KEY'] = secret + app.add_url_rule('/invoice//', 'getinvoice', getinvoice) + http_server = HTTPServer(WSGIContainer(app)) + http_server.listen(port) + IOLoop.instance().start() + return + + +jobs = {} + + +def start_server(port): + if port in jobs: + return False, "server already running" + + p = multiprocessing.Process( + target=worker, args=[port], name="server on port {}".format(port)) + p.daemon = True + + jobs[port] = p + p.start() + + return True + +def stop_server(port): + if port in jobs: + jobs[port].terminate() + del jobs[port] + return True + else: + return False + + +@plugin.method('invoiceserver') +def invoiceserver(request, command="start", port=8089): + """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/restart} and {port} .By default the plugin + starts the server on port 8089. + """ + commands = {"start", "stop", "restart", "list"} + + # if command unknown make start our default command + if command not in commands: + command = "start" + + # if port not an integer make 8088 as default + try: + port = int(port) + except: + port = int(plugin.options['invoice-web-port']['value']) + + if command == "list": + return "servers running on the following ports: {}".format(list(jobs.keys())) + + if command == "start": + if port in jobs: + return "Server already running on port {}. Maybe restart the server?".format(port) + suc = start_server(port) + if suc: + return "started server successfully on port {}".format(port) + else: + return "Could not start server on port {}".format(port) + + if command == "stop": + if stop_server(port): + return "stopped server on port{}".format(port) + else: + return "could not stop the server" + + if command == "restart": + stop_server(port) + suc = start_server(port) + if suc: + return "started server successfully on port {}".format(port) + else: + return "Could not start server on port {}".format(port) + +plugin.add_option( + 'invoiceserver-autostart', + 'true', + 'Should the invoice server start automatically' +) + +plugin.add_option( + 'invoiceserver-web-port', + '8809', + 'Which port should the invoice server listen to?' +) + + + +@plugin.init() +def init(options, configuration, plugin): + port = int(options['invoiceserver-web-port']) + + if options['invoiceserver-autostart'].lower() in ['true', '1']: + start_server(port) + + +plugin.run() diff --git a/request-invoice/requirements.txt b/request-invoice/requirements.txt new file mode 100644 index 0000000..fc95529 --- /dev/null +++ b/request-invoice/requirements.txt @@ -0,0 +1,4 @@ +flask +flask-limiter +tornado +pyln-client