mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 18:11:28 +01:00
daemon: pay command.
This is the command an actual user would use: it figures out the fee and route, and pays it if it can. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
21a29d9b4d
commit
69a8ea2ad9
@ -25,9 +25,10 @@ DAEMON_SRC := \
|
||||
daemon/netaddr.c \
|
||||
daemon/onion.c \
|
||||
daemon/opt_time.c \
|
||||
daemon/peer.c \
|
||||
daemon/packets.c \
|
||||
daemon/pay.c \
|
||||
daemon/payment.c \
|
||||
daemon/peer.c \
|
||||
daemon/routing.c \
|
||||
daemon/secrets.c \
|
||||
daemon/timeout.c \
|
||||
@ -60,6 +61,7 @@ DAEMON_HEADERS := \
|
||||
daemon/netaddr.h \
|
||||
daemon/onion.h \
|
||||
daemon/opt_time.h \
|
||||
daemon/pay.h \
|
||||
daemon/payment.h \
|
||||
daemon/peer.h \
|
||||
daemon/pseudorand.h \
|
||||
|
@ -1,9 +1,13 @@
|
||||
#ifndef LIGHTNING_DAEMON_CHAINTOPOLOGY_H
|
||||
#define LIGHTNING_DAEMON_CHAINTOPOLOGY_H
|
||||
#include "config.h"
|
||||
#include <ccan/short_types/short_types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct bitcoin_tx;
|
||||
struct lightningd_state;
|
||||
struct peer;
|
||||
struct sha256_double;
|
||||
struct txwatch;
|
||||
|
||||
/* This is the number of blocks which would have to be mined to invalidate
|
||||
|
@ -256,6 +256,7 @@ static const struct json_command *cmdlist[] = {
|
||||
&close_command,
|
||||
&newaddr_command,
|
||||
&accept_payment_command,
|
||||
&pay_command,
|
||||
/* Developer/debugging options. */
|
||||
&echo_command,
|
||||
&rhash_command,
|
||||
|
@ -72,4 +72,5 @@ extern const struct json_command output_command;
|
||||
extern const struct json_command accept_payment_command;
|
||||
extern const struct json_command add_route_command;
|
||||
extern const struct json_command routefail_command;
|
||||
extern const struct json_command pay_command;
|
||||
#endif /* LIGHTNING_DAEMON_JSONRPC_H */
|
||||
|
139
daemon/pay.c
Normal file
139
daemon/pay.c
Normal file
@ -0,0 +1,139 @@
|
||||
#include "chaintopology.h"
|
||||
#include "jsonrpc.h"
|
||||
#include "lightningd.h"
|
||||
#include "log.h"
|
||||
#include "onion.h"
|
||||
#include "pay.h"
|
||||
#include "peer.h"
|
||||
#include "routing.h"
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* Outstanding "pay" commands. */
|
||||
struct pay_command {
|
||||
struct list_node list;
|
||||
struct htlc *htlc;
|
||||
struct command *cmd;
|
||||
};
|
||||
|
||||
void complete_pay_command(struct peer *peer,
|
||||
struct htlc *htlc,
|
||||
const struct rval *rval)
|
||||
{
|
||||
struct pay_command *i;
|
||||
|
||||
list_for_each(&peer->pay_commands, i, list) {
|
||||
if (i->htlc == htlc) {
|
||||
if (rval) {
|
||||
struct json_result *response;
|
||||
|
||||
response = new_json_result(i->cmd);
|
||||
json_object_start(response, NULL);
|
||||
json_add_hex(response, "preimage",
|
||||
rval->r, sizeof(rval->r));
|
||||
json_object_end(response);
|
||||
command_success(i->cmd, response);
|
||||
} else {
|
||||
command_fail(i->cmd, "htlc failed");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Can happen if RPC connection goes away. */
|
||||
log_unusual(peer->log, "No command for HTLC %"PRIu64" %s",
|
||||
htlc->id, rval ? "fulfill" : "fail");
|
||||
}
|
||||
|
||||
static void remove_from_list(struct pay_command *pc)
|
||||
{
|
||||
list_del(&pc->list);
|
||||
}
|
||||
|
||||
static void json_pay(struct command *cmd,
|
||||
const char *buffer, const jsmntok_t *params)
|
||||
{
|
||||
struct pubkey id;
|
||||
jsmntok_t *idtok, *msatoshistok, *rhashtok;
|
||||
unsigned int expiry;
|
||||
int i;
|
||||
u64 msatoshis;
|
||||
s64 fee;
|
||||
struct sha256 rhash;
|
||||
struct node_connection **route;
|
||||
struct peer *peer;
|
||||
struct pay_command *pc;
|
||||
const u8 *onion;
|
||||
|
||||
if (!json_get_params(buffer, params,
|
||||
"id", &idtok,
|
||||
"msatoshis", &msatoshistok,
|
||||
"rhash", &rhashtok,
|
||||
NULL)) {
|
||||
command_fail(cmd, "Need id, msatoshis and rhash");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pubkey_from_hexstr(cmd->dstate->secpctx,
|
||||
buffer + idtok->start,
|
||||
idtok->end - idtok->start, &id)) {
|
||||
command_fail(cmd, "Invalid id");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json_tok_u64(buffer, msatoshistok, &msatoshis)) {
|
||||
command_fail(cmd, "'%.*s' is not a valid number",
|
||||
(int)(msatoshistok->end - msatoshistok->start),
|
||||
buffer + msatoshistok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hex_decode(buffer + rhashtok->start,
|
||||
rhashtok->end - rhashtok->start,
|
||||
&rhash, sizeof(rhash))) {
|
||||
command_fail(cmd, "'%.*s' is not a valid sha256 hash",
|
||||
(int)(rhashtok->end - rhashtok->start),
|
||||
buffer + rhashtok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: Add fee param, check for excessive fee. */
|
||||
peer = find_route(cmd->dstate, &id, msatoshis, &fee, &route);
|
||||
if (!peer) {
|
||||
command_fail(cmd, "no route found");
|
||||
return;
|
||||
}
|
||||
|
||||
expiry = 0;
|
||||
for (i = tal_count(route) - 1; i >= 0; i--) {
|
||||
expiry += route[i]->delay;
|
||||
if (expiry < route[i]->min_blocks)
|
||||
expiry = route[i]->min_blocks;
|
||||
}
|
||||
expiry += peer->nc->delay;
|
||||
if (expiry < peer->nc->min_blocks)
|
||||
expiry = peer->nc->min_blocks;
|
||||
|
||||
/* Expiry for HTLCs is absolute. And add one to give some margin. */
|
||||
expiry += get_block_height(cmd->dstate) + 1;
|
||||
|
||||
onion = onion_create(cmd, route, msatoshis, fee);
|
||||
pc = tal(cmd, struct pay_command);
|
||||
pc->cmd = cmd;
|
||||
pc->htlc = command_htlc_add(peer, msatoshis + fee, expiry, &rhash, NULL,
|
||||
onion);
|
||||
if (!pc->htlc) {
|
||||
command_fail(cmd, "could not add htlc");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait until we get response. */
|
||||
list_add_tail(&peer->pay_commands, &pc->list);
|
||||
tal_add_destructor(pc, remove_from_list);
|
||||
}
|
||||
|
||||
const struct json_command pay_command = {
|
||||
"pay",
|
||||
json_pay,
|
||||
"Send {id} {msatoshis} in return for preimage of {rhash}",
|
||||
"Returns an empty result on success"
|
||||
};
|
13
daemon/pay.h
Normal file
13
daemon/pay.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef LIGHTNING_DAEMON_PAY_H
|
||||
#define LIGHTNING_DAEMON_PAY_H
|
||||
#include "config.h"
|
||||
|
||||
struct peer;
|
||||
struct htlc;
|
||||
struct rval;
|
||||
|
||||
void complete_pay_command(struct peer *peer,
|
||||
struct htlc *htlc,
|
||||
const struct rval *rval);
|
||||
|
||||
#endif /* LIGHTNING_DAEMON_PAY_H */
|
@ -11,6 +11,7 @@
|
||||
#include "log.h"
|
||||
#include "names.h"
|
||||
#include "onion.h"
|
||||
#include "pay.h"
|
||||
#include "payment.h"
|
||||
#include "peer.h"
|
||||
#include "permute_tx.h"
|
||||
@ -567,11 +568,11 @@ static bool command_htlc_fulfill(struct peer *peer,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
unsigned int expiry,
|
||||
const struct sha256 *rhash,
|
||||
struct htlc *src,
|
||||
const u8 *route)
|
||||
struct htlc *command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
unsigned int expiry,
|
||||
const struct sha256 *rhash,
|
||||
struct htlc *src,
|
||||
const u8 *route)
|
||||
{
|
||||
struct channel_state *cstate;
|
||||
struct abs_locktime locktime;
|
||||
@ -579,19 +580,19 @@ static bool command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
|
||||
if (!blocks_to_abs_locktime(expiry, &locktime)) {
|
||||
log_unusual(peer->log, "add_htlc: fail: bad expiry %u", expiry);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (expiry < get_block_height(peer->dstate) + peer->dstate->config.min_htlc_expiry) {
|
||||
log_unusual(peer->log, "add_htlc: fail: expiry %u is too soon",
|
||||
expiry);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (expiry > get_block_height(peer->dstate) + peer->dstate->config.max_htlc_expiry) {
|
||||
log_unusual(peer->log, "add_htlc: fail: expiry %u is too far",
|
||||
expiry);
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: This is wrong: constraint on remote is sufficient. */
|
||||
@ -603,13 +604,13 @@ static bool command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
if (tal_count(peer->local.staging_cstate->side[OURS].htlcs) == 300
|
||||
|| tal_count(peer->remote.staging_cstate->side[OURS].htlcs) == 300) {
|
||||
log_unusual(peer->log, "add_htlc: fail: already at limit");
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!state_can_add_htlc(peer->state)) {
|
||||
log_unusual(peer->log, "add_htlc: fail: peer state %s",
|
||||
state_name(peer->state));
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
htlc = peer_new_htlc(peer, peer->htlc_id_counter,
|
||||
@ -628,8 +629,7 @@ static bool command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
log_unusual(peer->log, "add_htlc: fail: Cannot afford %"PRIu64
|
||||
" milli-satoshis in their commit tx",
|
||||
msatoshis);
|
||||
tal_free(htlc);
|
||||
return false;
|
||||
return tal_free(htlc);
|
||||
}
|
||||
tal_free(cstate);
|
||||
|
||||
@ -638,8 +638,7 @@ static bool command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
log_unusual(peer->log, "add_htlc: fail: Cannot afford %"PRIu64
|
||||
" milli-satoshis in our commit tx",
|
||||
msatoshis);
|
||||
tal_free(htlc);
|
||||
return false;
|
||||
return tal_free(htlc);
|
||||
}
|
||||
tal_free(cstate);
|
||||
|
||||
@ -648,7 +647,7 @@ static bool command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
/* Make sure we never offer the same one twice. */
|
||||
peer->htlc_id_counter++;
|
||||
|
||||
return true;
|
||||
return htlc;
|
||||
}
|
||||
|
||||
static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer)
|
||||
@ -658,8 +657,10 @@ static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer)
|
||||
|
||||
if (n == 0) {
|
||||
/* We close the connection once we've sent everything. */
|
||||
if (!state_can_io(peer->state))
|
||||
if (!state_can_io(peer->state)) {
|
||||
log_debug(peer->log, "pkt_out: no IO possible, closing");
|
||||
return io_close(conn);
|
||||
}
|
||||
return io_out_wait(conn, peer, pkt_out, peer);
|
||||
}
|
||||
|
||||
@ -825,6 +826,7 @@ static struct peer *new_peer(struct lightningd_state *dstate,
|
||||
peer->outpkt = tal_arr(peer, Pkt *, 0);
|
||||
peer->commit_jsoncmd = NULL;
|
||||
list_head_init(&peer->outgoing_txs);
|
||||
list_head_init(&peer->pay_commands);
|
||||
peer->close_watch_timeout = NULL;
|
||||
peer->anchor.watches = NULL;
|
||||
peer->cur_commit.watch = NULL;
|
||||
@ -1110,13 +1112,6 @@ const struct json_command connect_command = {
|
||||
"Returns an empty result on success"
|
||||
};
|
||||
|
||||
static void complete_pay_command(struct peer *peer,
|
||||
struct htlc *htlc,
|
||||
const struct rval *rval)
|
||||
{
|
||||
/* FIXME: implement. */
|
||||
}
|
||||
|
||||
/* FIXME: Keep a timeout for each peer, in case they're unresponsive. */
|
||||
|
||||
/* FIXME: Make sure no HTLCs in any unrevoked commit tx are live. */
|
||||
@ -2325,6 +2320,9 @@ static void route_htlc_onwards(struct peer *peer,
|
||||
struct pubkey id;
|
||||
struct peer *next;
|
||||
|
||||
log_debug_struct(peer->log, "Forwarding HTLC %s", struct sha256, &htlc->rhash);
|
||||
log_add(peer->log, " (id %"PRIu64")", htlc->id);
|
||||
|
||||
if (!proto_to_pubkey(peer->dstate->secpctx, pb_id, &id)) {
|
||||
log_unusual(peer->log,
|
||||
"Malformed pubkey for HTLC %"PRIu64, htlc->id);
|
||||
@ -2334,8 +2332,9 @@ static void route_htlc_onwards(struct peer *peer,
|
||||
|
||||
next = find_peer(peer->dstate, &id);
|
||||
if (!next || !next->nc) {
|
||||
log_unusual(peer->log, "Can't route HTLC %"PRIu64, htlc->id);
|
||||
log_add_struct(peer->log, " no peer %s", struct pubkey, &id);
|
||||
log_unusual(peer->log, "Can't route HTLC %"PRIu64": no %speer ",
|
||||
htlc->id, next ? "ready " : "");
|
||||
log_add_struct(peer->log, "%s", struct pubkey, &id);
|
||||
if (!peer->dstate->dev_never_routefail)
|
||||
command_htlc_fail(peer, htlc);
|
||||
return;
|
||||
@ -2352,6 +2351,8 @@ static void route_htlc_onwards(struct peer *peer,
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug_struct(peer->log, "HTLC forward to %s", struct pubkey, &next->id);
|
||||
|
||||
/* This checks the HTLC itself is possible. */
|
||||
if (!command_htlc_add(next, msatoshis,
|
||||
abs_locktime_to_blocks(&htlc->expiry)
|
||||
|
@ -127,6 +127,9 @@ struct peer {
|
||||
/* If we're doing a commit, this is the command which triggered it */
|
||||
struct command *commit_jsoncmd;
|
||||
|
||||
/* Any outstanding "pay" commands. */
|
||||
struct list_head pay_commands;
|
||||
|
||||
/* Global state. */
|
||||
struct lightningd_state *dstate;
|
||||
|
||||
@ -258,6 +261,12 @@ struct htlc *peer_new_htlc(struct peer *peer,
|
||||
struct htlc *src,
|
||||
enum channel_side side);
|
||||
|
||||
struct htlc *command_htlc_add(struct peer *peer, u64 msatoshis,
|
||||
unsigned int expiry,
|
||||
const struct sha256 *rhash,
|
||||
struct htlc *src,
|
||||
const u8 *route);
|
||||
|
||||
/* Peer has recieved revocation. */
|
||||
void peer_update_complete(struct peer *peer);
|
||||
|
||||
|
@ -9,6 +9,7 @@ scripts/setup.sh
|
||||
|
||||
DIR1=/tmp/lightning.$$.1
|
||||
DIR2=/tmp/lightning.$$.2
|
||||
DIR3=/tmp/lightning.$$.3
|
||||
|
||||
REDIR1="$DIR1/output"
|
||||
REDIR2="$DIR2/output"
|
||||
@ -81,6 +82,7 @@ done
|
||||
|
||||
LCLI1="../lightning-cli --lightning-dir=$DIR1"
|
||||
LCLI2="../lightning-cli --lightning-dir=$DIR2"
|
||||
LCLI3="../lightning-cli --lightning-dir=$DIR3"
|
||||
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
FGREP="fgrep"
|
||||
@ -105,6 +107,14 @@ lcli2()
|
||||
$LCLI2 "$@"
|
||||
}
|
||||
|
||||
lcli3()
|
||||
{
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
echo $LCLI3 "$@" >&2
|
||||
fi
|
||||
$LCLI3 "$@"
|
||||
}
|
||||
|
||||
blockheight()
|
||||
{
|
||||
$CLI getblockcount
|
||||
@ -217,9 +227,10 @@ all_ok()
|
||||
# Look for valgrind errors.
|
||||
if grep ^== $DIR1/errors; then exit 1; fi
|
||||
if grep ^== $DIR2/errors; then exit 1; fi
|
||||
if grep ^== $DIR3/errors; then exit 1; fi
|
||||
scripts/shutdown.sh
|
||||
|
||||
trap "rm -rf $DIR1 $DIR2" EXIT
|
||||
trap "rm -rf $DIR1 $DIR2 $DIR3" EXIT
|
||||
exit 0
|
||||
}
|
||||
|
||||
@ -228,7 +239,7 @@ if [ -n "$CRASH_ON_FAIL" ]; then
|
||||
else
|
||||
trap "echo Results in $DIR1 and $DIR2 >&2; cat $DIR1/errors $DIR2/errors >&2" EXIT
|
||||
fi
|
||||
mkdir $DIR1 $DIR2
|
||||
mkdir $DIR1 $DIR2 $DIR3
|
||||
|
||||
if [ -n "$MANUALCOMMIT" ]; then
|
||||
# Aka. never.
|
||||
@ -255,6 +266,8 @@ locktime-blocks=6
|
||||
commit-time=$COMMIT_TIME
|
||||
EOF
|
||||
|
||||
cp $DIR2/config $DIR3/config
|
||||
|
||||
if [ -n "$DIFFERENT_FEES" ]; then
|
||||
FEE_RATE2=300000
|
||||
CLOSE_FEE_RATE2=30000
|
||||
@ -275,6 +288,7 @@ if [ -n "$GDB2" ]; then
|
||||
else
|
||||
$PREFIX ../lightningd --lightning-dir=$DIR2 > $REDIR2 2> $REDIRERR2 &
|
||||
fi
|
||||
$PREFIX ../lightningd --lightning-dir=$DIR3 > $DIR3/output 2> $DIR3/errors &
|
||||
|
||||
if ! check "$LCLI1 getlog 2>/dev/null | $FGREP Hello"; then
|
||||
echo Failed to start daemon 1 >&2
|
||||
@ -286,10 +300,17 @@ if ! check "$LCLI2 getlog 2>/dev/null | $FGREP Hello"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! check "$LCLI3 getlog 2>/dev/null | $FGREP Hello"; then
|
||||
echo Failed to start daemon 3 >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID1=`$LCLI1 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'`
|
||||
ID2=`$LCLI2 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'`
|
||||
ID3=`$LCLI3 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'`
|
||||
|
||||
PORT2=`$LCLI2 getlog | sed -n 's/.*on port \([0-9]*\).*/\1/p'`
|
||||
PORT3=`$LCLI3 getlog | sed -n 's/.*on port \([0-9]*\).*/\1/p'`
|
||||
|
||||
# Make a payment into a P2SH for anchor.
|
||||
P2SHADDR=`$LCLI1 newaddr | sed -n 's/{ "address" : "\(.*\)" }/\1/p'`
|
||||
@ -767,6 +788,49 @@ lcli1 newhtlc $ID2 $(($HTLC_AMOUNT - 1)) $EXPIRY $RHASH4
|
||||
check lcli2 "getlog | $FGREP 'Short payment for HTLC'"
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE ""
|
||||
|
||||
if [ ! -n "$MANUALCOMMIT" ]; then
|
||||
# Test routing to a third node.
|
||||
P2SHADDR2=`$LCLI2 newaddr | sed -n 's/{ "address" : "\(.*\)" }/\1/p'`
|
||||
TXID2=`$CLI sendtoaddress $P2SHADDR2 0.01`
|
||||
TX2=`$CLI getrawtransaction $TXID2`
|
||||
$CLI generate 1
|
||||
|
||||
lcli2 connect localhost $PORT3 $TX2
|
||||
check_tx_spend lcli2
|
||||
$CLI generate 3
|
||||
|
||||
# Make sure it's STATE_NORMAL.
|
||||
check_peerstate lcli3 STATE_NORMAL
|
||||
|
||||
# More than enough to cover commit fees.
|
||||
HTLC_AMOUNT=100000000
|
||||
|
||||
# Tell node 1 about the 2->3 route.
|
||||
lcli1 add-route $ID2 $ID3 546000 10 36 36
|
||||
RHASH5=`lcli3 accept-payment $HTLC_AMOUNT | sed 's/.*"\([0-9a-f]*\)".*/\1/'`
|
||||
|
||||
# Try wrong hash.
|
||||
if lcli1 pay $ID3 $HTLC_AMOUNT $RHASH4; then
|
||||
echo Paid with wrong hash? >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try underpaying.
|
||||
if lcli1 pay $ID3 $(($HTLC_AMOUNT-1)) $RHASH5; then
|
||||
echo Paid with too little? >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Pay correctly.
|
||||
lcli1 pay $ID3 $HTLC_AMOUNT $RHASH5
|
||||
|
||||
# Node 3 should end up with that amount (minus 1/2 tx fee)
|
||||
# Note that it is delayed a little, since node2 fulfils as soon as fulfill
|
||||
# starts.
|
||||
check lcli3 "getpeers | $FGREP \"\\\"our_amount\\\" : $(($HTLC_AMOUNT - $NO_HTLCS_FEE / 2))\""
|
||||
lcli3 close $ID2
|
||||
fi
|
||||
|
||||
lcli1 close $ID2
|
||||
|
||||
# They should be negotiating the close.
|
||||
|
Loading…
Reference in New Issue
Block a user