1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-22 14:22:39 +01:00
eclair/eclair-core/eclair-cli
Pierre-Marie Padiou 9032da5326
Add a proper payments database (#885)
There is no unique identifier for payments in LN protocol. Critically,
we can't use `payment_hash` as a unique id because there is no way to
ensure unicity at the protocol level.

Also, the general case for a "payment" is to be associated to multiple
`update_add_htlc`s, because of automated retries. We also routinely
retry payments, which means that the same `payment_hash` will be
conceptually linked to a list of lists of `update_add_htlc`s.

In order to address this, we introduce a payment id, which uniquely
identifies a payment, as in a set of sequential `update_add_htlc`
managed by a single `PaymentLifecycle` that ends with a `PaymentSent` or
`PaymentFailed` outcome.

We can then query the api using either `payment_id` or `payment_hash`.
The former will return a single payment status, the latter will return a
set of payment statuses, each identified by their `payment_id`.

* Add a payment identifier

* Remove InvalidPaymentHash channel exception

* Remove unused 'close' from paymentsDb

* Introduce sent_payments in PaymentDB, bump db version

* Return the UUID of the ongoing payment in /send API

* Add api to query payments by ID

* Add 'fallbackAddress' in /receive API

* Expose /paymentinfo by paymentHash

* Add id column to audit.sent table, add test for db migration

* Add invoices to payment DB

* Add license header to ExtraDirective.scala

* Respond with HTTP 404 if the corresponding invoice/paymentHash was not found.

* Left-pad numeric bolt11 tagged fields to have a number of bits multiple of five (bech32 encoding).

* Add invoices API

* Remove CheckPayment message

* GUI: consume UUID reply from payment initiator

* API: reply with JSON encoded response if the queried element wasn't found

* Return a payment request object in /receive

* Remove limit of pending payment requests!

* Avoid printing "null" fields when serializing an invoice to json

* Add index on paymentDb.sent_payments.payment_hash

* Order results in descending order in listPaymentRequest
2019-04-16 17:03:21 +02:00

109 lines
3.8 KiB
Bash
Executable file

#!/bin/bash
# default script values, can be overriden for convenience.
api_url='http://localhost:8080'
# uncomment the line below if you don't want to provide a password each time you call eclair-cli
# api_password='your_api_password'
# for some commands the json output can be shortened for better readability
short=false
# prints help message
usage() {
echo -e "==============================
Command line client for eclair
==============================
This tool requires the eclair node's API to be enabled and listening
on <$api_url>.
Usage
-----
\e[93meclair-cli\e[39m [\e[93mOPTIONS\e[39m]... <\e[93mCOMMAND\e[39m> [--command-param=command-value]...
where OPTIONS can be:
-p <password> API's password
-a <address> Override the API URL with <address>
-h Show this help
-s Some commands can print a trimmed JSON
and COMMAND is one of:
getinfo, connect, open, close, forceclose, updaterelayfee,
peers, channels, channel, allnodes, allchannels, allupdates,
receive, parseinvoice, findroute, findroutetonode,
payinvoice, sendtonode, getreceivedinfo, audit, networkfees,
channelstats, getsentinfo, getinvoice, allinvoice, listpendinginvoices
Examples
--------
eclair-cli -a localhost:1234 peers list the peers of a node hosted on localhost:1234
eclair-cli close --channelId=006fb... closes the channel with id 006fb...
Full documentation here: <https://acinq.github.io/eclair>" 1>&2;
exit 1;
}
# -- script's logic begins here
# Check if jq is installed. If not, display instructions and abort program
command -v jq >/dev/null 2>&1 || { echo -e "This tool requires jq.\nFor installation instructions, visit https://stedolan.github.io/jq/download/.\n\nAborting..."; exit 1; }
# curl installed? If not, give a hint
command -v curl >/dev/null 2>&1 || { echo -e "This tool requires curl.\n\nAborting..."; exit 1; }
# extract script options
while getopts ':cu:su:p:a:hu:' flag; do
case "${flag}" in
p) api_password="${OPTARG}" ;;
a) api_url="${OPTARG}" ;;
h) usage ;;
s) short=true ;;
*) ;;
esac
done
shift $(($OPTIND - 1))
# extract api's endpoint (e.g. sendpayment, connect, ...) from params
api_endpoint=${1}
shift 1
# display a usage method if no method given or help requested
if [ -z $api_endpoint ] || [ "$api_endpoint" == "help" ]; then
usage;
fi
# long options are expected to be of format: --param=param_value
api_payload=""
for arg in "${@}"; do
case ${arg} in
"--"*) api_payload="$api_payload --data-urlencode \"${arg:2}\"";
;;
*) echo "incorrect argument, please use --arg=value"; usage;
;;
esac
done;
# jq filter parses response body for error message
jq_filter='if type=="object" and .error != null then .error else .';
# apply special jq filter if we are in "short" ouput mode -- only for specific commands such as 'channels'
if [ "$short" = true ]; then
jq_channel_filter="{ nodeId, shortChannelId: .data.shortChannelId, channelId, state, balanceSat: (try (.data.commitments.localCommit.spec.toLocalMsat / 1000 | floor) catch null), capacitySat: .data.commitments.commitInput.amountSatoshis, channelPoint: .data.commitments.commitInput.outPoint }";
case $api_endpoint in
"channels") jq_filter="$jq_filter | map( $jq_channel_filter )" ;;
"channel") jq_filter="$jq_filter | $jq_channel_filter" ;;
*) ;;
esac
fi
jq_filter="$jq_filter end";
# if no password is provided, auth should only contain user login so that curl prompts for the api password
if [ -z $api_password ]; then
auth="eclair-cli";
else
auth="eclair-cli:$api_password";
fi
# we're now ready to execute the API call
eval curl "--user $auth --silent --show-error -X POST -H \"Content-Type: application/x-www-form-urlencoded\" $api_payload $api_url/$api_endpoint" | jq -r "$jq_filter"