From dab33740d0d2f681a3e3d2d7fa8e5d3c97bc578e Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Sat, 15 Jun 2024 12:37:15 -0700 Subject: [PATCH] Initialize repo for lightning experiments. --- .gitignore | 6 +++ README.md | 1 + docs/lightningd.md | 90 +++++++++++++++++++++++++++++++++ docs/lnd.md | 11 ++++ lnd-get-info.py | 14 ++++++ lnd-get-info.sh | 3 ++ lnd-get-info.ts | 41 +++++++++++++++ package-lock.json | 122 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 16 ++++++ 9 files changed, 304 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docs/lightningd.md create mode 100644 docs/lnd.md create mode 100644 lnd-get-info.py create mode 100644 lnd-get-info.sh create mode 100644 lnd-get-info.ts create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a19ac6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode +.venv +archive +node_modules +assets +*-test.ts \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7f2a27 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# lightning payment experiments diff --git a/docs/lightningd.md b/docs/lightningd.md new file mode 100644 index 0000000..9b7ba5b --- /dev/null +++ b/docs/lightningd.md @@ -0,0 +1,90 @@ +# lightning payments + +Simple payments app for core-lightning node (lightningd). + +## Usage + +* Start lightningd with the new rest api `--clnrest-port=3010` or configure it in `.lightning/config` + * Note that the clnrest plugin is bundled within lightningd, but its dependencies are not. You might need to `pip install -r requirements.txt` before it'll work. + * When the plugin is working, the lightningd log will show a message like `plugin-clnrest.py: REST server running at https://127.0.0.1:3010` +* Create a run with `lightning-cli commando-rune` + + ```shell + lightning-cli commando-rune restrictions='[["method^list", "method^get", "method=summary", "method~invoice", "method~offers"],["method/listdatastore"]]' + export RUNE= + curl -X GET 'http://localhost:3010/v1/list-methods' -H "Rune: ${RUNE}" + curl -k -X POST 'http://localhost:3010/v1/getinfo' -H "Rune: ${RUNE}" + curl -X POST 'http://localhost:3010/v1/listoffers' -H "Rune: ${RUNE}" + curl -X POST 'http://localhost:3010/v1/listinvoices' -H "Rune: ${RUNE}" + deno run --allow-net --unsafely-ignore-certificate-errors test.ts + ``` + +* TODO: env var + +## Resources + +Swagger for lightningd will be on `https://192.168.0.42:3010` + +* c-lightning-REST [API Docs](https://squirtle.satstack.net:4446/api-docs/) +* CLNRest [API Reference](https://docs.corelightning.org/reference/get_list_methods_resource) +* [CLNRest](https://docs.corelightning.org/docs/rest) has examples + +## Copypasta + +To run node.js in repl: + +```sh +npm init -y +npm install fs socket.io-client +node +``` + +Paste code. + +To run code in Deno: + +```shell +deno repl --allow-net --unsafely-ignore-certificate-errors +``` + +## Decisions + +### SQLite + +Pros + +* postgres typically wants a lot more memory, which motivates running it on a separate server +* some of our custom strfry-policies read from the same database, and strfry needs to reload the policies to import updates to the pubkey lists +* avoids introducing more failure methods, such as failing to connect to an external postgresql database +* reduces barrier to entry for other relay ops who might attempt to run the code themselves + +Cons + +* downsides to this choice includes increasing overhead memory requirements for the server, but the whole purpose of this code is to fund a better server anyway +* another downside is that the Deno sqlite integrations don't seem to be very mature + +## Discovery Notes + +Relevant methods + +* Command: waitanyinvoice [lastpay_index] [timeout] + * Description: Wait for the next invoice to be paid, after {lastpay_index} (if supplied). If {timeout} seconds is reached while waiting, fail with an error. + +* Command: waitinvoice label + * Description: Wait for an incoming payment matching the invoice with {label}, or if the invoice expires + +* Command: listinvoices [label] [invstring] [payment_hash] [offer_id] [index] [start] [limit] + * Description: Show invoice matching {label}, {invstring}, {payment_hash} or {offerid} (or all, if no query parameter specified) + +* Command: invoice amount_msat label description [expiry] [fallbacks] [preimage] [exposeprivatechannels] [cltv] [deschashonly] [dev-routes] + * Description: Create an invoice for {msatoshi} with {label} and {description} with optional {expiry} seconds (default 1 week), optional {fallbacks} address list(default empty list) and optional {preimage} (default autogenerated) + + +### Invoices + +Generate one + +```shell +lightning-cli invoice amount_msat=$((2 *1000)) label=$(openssl rand -hex 4) description="test 2" expiry=3600 +curl -X POST 'http://localhost:3010/v1/listinvoices' -H "Rune: ${RUNE}" +``` diff --git a/docs/lnd.md b/docs/lnd.md new file mode 100644 index 0000000..5c223c6 --- /dev/null +++ b/docs/lnd.md @@ -0,0 +1,11 @@ +# lnd invoices + +Use `--private` to fetch an invoice with route hints for a node that only has unannounced channels: + +```sh +lncli addinvoice --private --expiry 3600 --memo npub1dxs2pygtfxsah77yuncsmu3ttqr274qr5g5zva3c7t5s3jtgy2xszsn4st 10 +``` + +## Resources + +* [REST API Documentation](https://lightning.engineering/api-docs/api/lnd/lightning/get-info) diff --git a/lnd-get-info.py b/lnd-get-info.py new file mode 100644 index 0000000..4fa172b --- /dev/null +++ b/lnd-get-info.py @@ -0,0 +1,14 @@ +from codecs import encode +import json +import requests + +REST_HOST = '192.168.0.42:10010' +MACAROON_PATH = 'assets/readonly.macaroon' +TLS_PATH = 'assets/tls.cert' + +url = f'https://{REST_HOST}/v1/getinfo' +macaroon = encode(open(MACAROON_PATH, 'rb').read(), 'hex') +headers = {'Grpc-Metadata-macaroon': macaroon} +r = requests.get(url, headers=headers, verify=TLS_PATH) +lnd_info = json.loads(r.text) +print(lnd_info) \ No newline at end of file diff --git a/lnd-get-info.sh b/lnd-get-info.sh new file mode 100644 index 0000000..6713d15 --- /dev/null +++ b/lnd-get-info.sh @@ -0,0 +1,3 @@ +curl --cacert assets/tls.cert \ + -H "Grpc-Metadata-macaroon: $(xxd -ps -u -c 10000 assets/readonly.macaroon)" \ + https://192.168.0.42:10010/v1/getinfo | jq diff --git a/lnd-get-info.ts b/lnd-get-info.ts new file mode 100644 index 0000000..ff4c2c2 --- /dev/null +++ b/lnd-get-info.ts @@ -0,0 +1,41 @@ +import { encodeHex } from "https://deno.land/std@0.224.0/encoding/hex.ts"; + +// Constants +const REST_HOST = '192.168.0.42:10010'; +const MACAROON_PATH = 'assets/readonly.macaroon'; +const TLS_PATH = 'assets/tls.cert'; + +const url = `https://${REST_HOST}/v1/getinfo`; +//const macaroonBytes = Deno.readTextFileSync(MACAROON_PATH); +//const macaroon = encodeHex(macaroonBytes).toString(); +//console.log(macaroon); + +using file = Deno.openSync(MACAROON_PATH, { read: true, write: false }); + +// Configure headers +const headers = new Headers({ + 'Grpc-Metadata-macaroon': macaroon, +}); + +// Function to make the GET request +async function getLndInfo() { + const response = await fetch(url, { + method: "GET", + headers: headers, + }); + + // Check if the response is OK + if (!response.ok) { + console.error("Failed to fetch:", response.status); + return; + } + + // Parse the JSON response + const lndInfo = await response.json(); + console.log(lndInfo); +} + +// Execute the function +getLndInfo(); + +file.close(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f6c0295 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,122 @@ +{ + "name": "npub-pay-clnrest", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "npub-pay-clnrest", + "version": "0.0.1", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "socket.io-client": "^4.7.5" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz", + "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5ee1883 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "npub-pay-clnrest", + "version": "0.0.1", + "description": "Simple payments app for core-lightning node.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "socket.io-client": "^4.7.5" + } +}