diff --git a/daemon/Makefile b/daemon/Makefile index 9dacfd579..3492bf103 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -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 \ diff --git a/daemon/chaintopology.h b/daemon/chaintopology.h index 75977bd15..ea6ff13f1 100644 --- a/daemon/chaintopology.h +++ b/daemon/chaintopology.h @@ -1,9 +1,13 @@ #ifndef LIGHTNING_DAEMON_CHAINTOPOLOGY_H #define LIGHTNING_DAEMON_CHAINTOPOLOGY_H #include "config.h" +#include #include +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 diff --git a/daemon/jsonrpc.c b/daemon/jsonrpc.c index 9d3fb09aa..d30347aaa 100644 --- a/daemon/jsonrpc.c +++ b/daemon/jsonrpc.c @@ -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, diff --git a/daemon/jsonrpc.h b/daemon/jsonrpc.h index aa37bf9dc..960706522 100644 --- a/daemon/jsonrpc.h +++ b/daemon/jsonrpc.h @@ -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 */ diff --git a/daemon/pay.c b/daemon/pay.c new file mode 100644 index 000000000..7bf139185 --- /dev/null +++ b/daemon/pay.c @@ -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 +#include + +/* 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" +}; diff --git a/daemon/pay.h b/daemon/pay.h new file mode 100644 index 000000000..85f28a202 --- /dev/null +++ b/daemon/pay.h @@ -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 */ diff --git a/daemon/peer.c b/daemon/peer.c index 6ad1dcb91..2d31878b0 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -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) diff --git a/daemon/peer.h b/daemon/peer.h index 065d8bbbc..2b8651abe 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -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); diff --git a/daemon/test/test.sh b/daemon/test/test.sh index 74e153fb1..972ce1a8c 100755 --- a/daemon/test/test.sh +++ b/daemon/test/test.sh @@ -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.