diff --git a/lightningd/Makefile b/lightningd/Makefile index 5fe7d5a8e..b8a25f35d 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -126,7 +126,7 @@ check-makefile: check-lightningd-makefile check-lightningd-makefile: @for f in lightningd/*.h lightningd/*/*.h; do if ! echo $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) "" | grep -q "$$f "; then echo $$f not mentioned in LIGHTNINGD_HEADERS_NOGEN or LIGHTNINGD_HEADERS_GEN >&2; exit 1; fi; done -lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CCAN_OBJS) $(CCAN_SHACHAIN48_OBJ) $(LIGHTNINGD_HSM_CONTROL_OBJS) $(LIGHTNINGD_HANDSHAKE_CONTROL_OBJS) $(LIGHTNINGD_GOSSIP_CONTROL_OBJS) $(LIBBASE58_OBJS) $(LIGHTNINGD_OPENING_CONTROL_OBJS) $(LIGHTNINGD_CHANNEL_CONTROL_OBJS) libsecp256k1.a libsodium.a libwallycore.a +lightningd/lightningd: $(LIGHTNINGD_OBJS) $(LIGHTNINGD_OLD_OBJS) $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_JSMN_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(WIRE_ONION_OBJS) $(CCAN_OBJS) $(CCAN_SHACHAIN48_OBJ) $(LIGHTNINGD_HSM_CONTROL_OBJS) $(LIGHTNINGD_HANDSHAKE_CONTROL_OBJS) $(LIGHTNINGD_GOSSIP_CONTROL_OBJS) $(LIBBASE58_OBJS) $(LIGHTNINGD_OPENING_CONTROL_OBJS) $(LIGHTNINGD_CHANNEL_CONTROL_OBJS) libsecp256k1.a libsodium.a libwallycore.a clean: lightningd-clean diff --git a/lightningd/channel/channel.c b/lightningd/channel/channel.c index 3f522dcb9..9467b95b5 100644 --- a/lightningd/channel/channel.c +++ b/lightningd/channel/channel.c @@ -722,6 +722,44 @@ static void handle_peer_fulfill_htlc(struct peer *peer, const u8 *msg) abort(); } +static void handle_peer_fail_htlc(struct peer *peer, const u8 *msg) +{ + struct channel_id channel_id; + u64 id; + enum channel_remove_err e; + u8 *reason; + + if (!fromwire_update_fail_htlc(msg, msg, NULL, + &channel_id, &id, &reason)) { + peer_failed(io_conn_fd(peer->peer_conn), + &peer->pcs.cs, + &peer->channel_id, + WIRE_CHANNEL_PEER_BAD_MESSAGE, + "Bad update_fulfill_htlc %s", tal_hex(msg, msg)); + } + + e = channel_fail_htlc(peer->channel, LOCAL, id); + switch (e) { + case CHANNEL_ERR_REMOVE_OK: + msg = towire_channel_failed_htlc(msg, id, reason); + daemon_conn_send(&peer->master, take(msg)); + start_commit_timer(peer); + return; + case CHANNEL_ERR_NO_SUCH_ID: + case CHANNEL_ERR_ALREADY_FULFILLED: + case CHANNEL_ERR_HTLC_UNCOMMITTED: + case CHANNEL_ERR_HTLC_NOT_IRREVOCABLE: + case CHANNEL_ERR_BAD_PREIMAGE: + peer_failed(io_conn_fd(peer->peer_conn), + &peer->pcs.cs, + &peer->channel_id, + WIRE_CHANNEL_PEER_BAD_MESSAGE, + "Bad update_fail_htlc: failed to remove %" + PRIu64 " error %u", id, e); + } + abort(); +} + static struct io_plan *peer_in(struct io_conn *conn, struct peer *peer, u8 *msg) { enum wire_type type = fromwire_peektype(msg); @@ -767,6 +805,9 @@ static struct io_plan *peer_in(struct io_conn *conn, struct peer *peer, u8 *msg) case WIRE_UPDATE_FULFILL_HTLC: handle_peer_fulfill_htlc(peer, msg); goto done; + case WIRE_UPDATE_FAIL_HTLC: + handle_peer_fail_htlc(peer, msg); + goto done; case WIRE_INIT: case WIRE_ERROR: case WIRE_OPEN_CHANNEL: @@ -777,7 +818,6 @@ static struct io_plan *peer_in(struct io_conn *conn, struct peer *peer, u8 *msg) case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: - case WIRE_UPDATE_FAIL_HTLC: case WIRE_UPDATE_FAIL_MALFORMED_HTLC: case WIRE_UPDATE_FEE: peer_failed(io_conn_fd(peer->peer_conn), @@ -1011,12 +1051,14 @@ static void handle_fail(struct peer *peer, const u8 *inmsg) u8 *msg; u64 id; u8 *errpkt; + enum channel_remove_err e; if (!fromwire_channel_fail_htlc(inmsg, inmsg, NULL, &id, &errpkt)) status_failed(WIRE_CHANNEL_BAD_COMMAND, "Invalid channel_fail_htlc"); - switch (channel_fail_htlc(peer->channel, REMOTE, id)) { + e = channel_fail_htlc(peer->channel, REMOTE, id); + switch (e) { case CHANNEL_ERR_REMOVE_OK: msg = towire_update_fail_htlc(peer, &peer->channel_id, id, errpkt); @@ -1032,7 +1074,7 @@ static void handle_fail(struct peer *peer, const u8 *inmsg) case CHANNEL_ERR_HTLC_NOT_IRREVOCABLE: case CHANNEL_ERR_BAD_PREIMAGE: status_failed(WIRE_CHANNEL_BAD_COMMAND, - "HTLC %"PRIu64" preimage failed", id); + "HTLC %"PRIu64" removal failed: %i", id, e); } abort(); } @@ -1080,7 +1122,8 @@ static struct io_plan *req_in(struct io_conn *conn, struct daemon_conn *master) case WIRE_CHANNEL_PEER_BAD_MESSAGE: break; } - status_failed(WIRE_CHANNEL_BAD_COMMAND, "%s", strerror(errno)); + status_failed(WIRE_CHANNEL_BAD_COMMAND, "%u %s", t, + channel_wire_type_name(t)); } out: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 7d756d4b4..ac63b03d6 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -635,11 +635,24 @@ struct decoding_htlc { u8 shared_secret[32]; }; -static void fail_htlc(struct peer *peer, u64 htlc_id, enum onion_type failcode) +static void fail_htlc(struct peer *peer, u64 htlc_id, const u8 *msg) { - log_broken(peer->log, "failed htlc %"PRIu64" code 0x%04x", - htlc_id, failcode); - /* FIXME: implement */ + enum onion_type failcode = fromwire_peektype(msg); + + log_broken(peer->log, "failed htlc %"PRIu64" code 0x%04x (%s)", + htlc_id, failcode, onion_type_name(failcode)); + + /* We don't do BADONION here */ + assert(!(failcode & BADONION)); + if (failcode & UPDATE) { + /* FIXME: Ask gossip daemon for channel_update. */ + } + + /* FIXME: encrypt msg! */ + subd_send_msg(peer->owner, + take(towire_channel_fail_htlc(peer, htlc_id, msg))); + if (taken(msg)) + tal_free(msg); } static void handle_localpay(struct htlc_end *hend, @@ -651,7 +664,8 @@ static void handle_localpay(struct htlc_end *hend, payment_hash); if (!invoice) { - fail_htlc(hend->peer, hend->htlc_id, WIRE_UNKNOWN_PAYMENT_HASH); + fail_htlc(hend->peer, hend->htlc_id, + take(towire_unknown_payment_hash(hend))); tal_free(hend); return; } @@ -666,11 +680,11 @@ static void handle_localpay(struct htlc_end *hend, */ if (hend->msatoshis < invoice->msatoshi) { fail_htlc(hend->peer, hend->htlc_id, - WIRE_INCORRECT_PAYMENT_AMOUNT); + take(towire_incorrect_payment_amount(hend))); return; } else if (hend->msatoshis > invoice->msatoshi * 2) { fail_htlc(hend->peer, hend->htlc_id, - WIRE_INCORRECT_PAYMENT_AMOUNT); + take(towire_incorrect_payment_amount(hend))); return; } @@ -685,7 +699,8 @@ static void handle_localpay(struct htlc_end *hend, cltv_expiry, get_block_height(hend->peer->ld->topology), hend->peer->ld->dstate.config.deadline_blocks); - fail_htlc(hend->peer, hend->htlc_id, WIRE_FINAL_EXPIRY_TOO_SOON); + fail_htlc(hend->peer, hend->htlc_id, + take(towire_final_expiry_too_soon(hend))); tal_free(hend); return; } @@ -773,6 +788,40 @@ static int peer_fulfilled_htlc(struct peer *peer, const u8 *msg) return 0; } +static int peer_failed_htlc(struct peer *peer, const u8 *msg) +{ + u64 id; + u8 *reason; + struct htlc_end *hend; + enum onion_type failcode; + + if (!fromwire_channel_failed_htlc(msg, msg, NULL, &id, &reason)) { + log_broken(peer->log, "bad fromwire_channel_failed_htlc %s", + tal_hex(peer, msg)); + return -1; + } + + hend = find_htlc_end(&peer->ld->htlc_ends, peer, id, HTLC_DST); + if (!hend) { + log_broken(peer->log, + "channel_failed_htlc unknown htlc %"PRIu64, + id); + return -1; + } + + /* FIXME: Decrypt reason. */ + failcode = fromwire_peektype(reason); + + log_info(peer->log, "htlc %"PRIu64" failed with code 0x%04x (%s)", + id, failcode, onion_type_name(failcode)); + + /* FIXME: Forward! */ + assert(!hend->other_end); + tal_free(hend); + + return 0; +} + static int channel_msg(struct subd *sd, const u8 *msg, const int *unused) { enum channel_wire_type t = fromwire_peektype(msg); @@ -789,6 +838,7 @@ static int channel_msg(struct subd *sd, const u8 *msg, const int *unused) case WIRE_CHANNEL_FULFILLED_HTLC: return peer_fulfilled_htlc(sd->peer, msg); case WIRE_CHANNEL_FAILED_HTLC: + return peer_failed_htlc(sd->peer, msg); case WIRE_CHANNEL_MALFORMED_HTLC: /* FIXME: Forward. */ abort(); diff --git a/lightningd/test/test-basic b/lightningd/test/test-basic index 43f2199f7..7f7b8551b 100755 --- a/lightningd/test/test-basic +++ b/lightningd/test/test-basic @@ -63,6 +63,7 @@ check "lcli1 getlog debug | $FGREP 'Sending commit_sig with 0 htlc sigs'" check "lcli2 getlog debug | $FGREP 'their htlc 0 locked'" check "lcli2 getpeers info | $FGREP 'failed htlc 0 code 0x400f'" +check "lcli1 getpeers info | $FGREP 'htlc 0 failed with code 0x400f'" # This one isn't dust. RHASH=`lcli2 invoice 100000000 testpayment1 | get_field rhash`