From b2fdc86740195300a901064f1f85641010a8c540 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 Jun 2016 09:08:10 +0930 Subject: [PATCH] daemon: check and use routing info in HTLC packet. We only support being the end node for the moment. Signed-off-by: Rusty Russell --- daemon/packets.c | 3 +- daemon/peer.c | 113 ++++++++++++++++++++++++++++++++++++-------- daemon/test/test.sh | 11 ++++- 3 files changed, 104 insertions(+), 23 deletions(-) diff --git a/daemon/packets.c b/daemon/packets.c index ee04e8282..0ff3ffddc 100644 --- a/daemon/packets.c +++ b/daemon/packets.c @@ -188,9 +188,10 @@ void queue_pkt_htlc_add(struct peer *peer, if (!blocks_to_abs_locktime(expiry, &locktime)) fatal("Invalid locktime?"); u->expiry = abs_locktime_to_proto(u, &locktime); - /* FIXME: routing! */ u->route = tal(u, Routing); routing__init(u->route); + u->route->info.data = tal_dup_arr(u, u8, route, tal_count(route), 0); + u->route->info.len = tal_count(u->route->info.data); /* BOLT #2: * diff --git a/daemon/peer.c b/daemon/peer.c index 891d68bc3..2a04654b1 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -534,6 +534,7 @@ static void state_event(struct peer *peer, } } +/* FIXME: Reason! */ static bool command_htlc_fail(struct peer *peer, u64 id) { if (!state_can_remove_htlc(peer->state)) @@ -2251,6 +2252,96 @@ static const char *owner_name(enum channel_side side) return side == OURS ? "our" : "their"; } +static void route_htlc_onwards(struct peer *peer, + const struct channel_htlc *htlc, + u64 msatoshis, + const BitcoinPubkey *pb_id, + const u8 *rest_of_route) +{ + /* FIXME: implement */ +} + +static void their_htlc_added(struct peer *peer, const struct channel_htlc *htlc) +{ + RouteStep *step; + const u8 *rest_of_route; + struct payment *payment; + + if (abs_locktime_is_seconds(&htlc->expiry)) { + log_unusual(peer->log, "HTLC %"PRIu64" is in seconds", htlc->id); + command_htlc_fail(peer, htlc->id); + return; + } + + if (abs_locktime_to_blocks(&htlc->expiry) <= + get_block_height(peer->dstate) + peer->dstate->config.min_htlc_expiry) { + log_unusual(peer->log, "HTLC %"PRIu64" expires too soon:" + " block %u", + htlc->id, abs_locktime_to_blocks(&htlc->expiry)); + command_htlc_fail(peer, htlc->id); + return; + } + + if (abs_locktime_to_blocks(&htlc->expiry) > + get_block_height(peer->dstate) + peer->dstate->config.max_htlc_expiry) { + log_unusual(peer->log, "HTLC %"PRIu64" expires too far:" + " block %u", + htlc->id, abs_locktime_to_blocks(&htlc->expiry)); + command_htlc_fail(peer, htlc->id); + return; + } + + step = onion_unwrap(peer, htlc->routing, tal_count(htlc->routing), + &rest_of_route); + if (!step) { + log_unusual(peer->log, "Bad onion, failing HTLC %"PRIu64, + htlc->id); + command_htlc_fail(peer, htlc->id); + return; + } + + switch (step->next_case) { + case ROUTE_STEP__NEXT_END: + payment = find_payment(peer->dstate, &htlc->rhash); + if (!payment) { + log_unusual(peer->log, "No payment for HTLC %"PRIu64, + htlc->id); + log_add_struct(peer->log, " rhash=%s", + struct sha256, &htlc->rhash); + if (unlikely(!peer->dstate->dev_never_routefail)) + command_htlc_fail(peer, htlc->id); + goto free_rest; + } + + if (htlc->msatoshis != payment->msatoshis) { + log_unusual(peer->log, "Short payment for HTLC %"PRIu64 + ": %"PRIu64" not %"PRIu64 " satoshi!", + htlc->id, + htlc->msatoshis, + payment->msatoshis); + command_htlc_fail(peer, htlc->id); + return; + } + + log_info(peer->log, "Immediately resolving HTLC %"PRIu64, + htlc->id); + command_htlc_fulfill(peer, htlc->id, &payment->r); + goto free_rest; + + case ROUTE_STEP__NEXT_BITCOIN: + route_htlc_onwards(peer, htlc, step->amount, step->bitcoin, + rest_of_route); + goto free_rest; + default: + log_info(peer->log, "Unknown step type %u", step->next_case); + command_htlc_fail(peer, htlc->id); + goto free_rest; + } + +free_rest: + tal_free(rest_of_route); +} + /* When changes are committed to. */ void peer_both_committed_to(struct peer *peer, const union htlc_staging *changes, @@ -2311,29 +2402,9 @@ void peer_both_committed_to(struct peer *peer, return; for (i = 0; i < n; i++) { - struct payment *payment; - switch (changes[i].type) { case HTLC_ADD: - payment = find_payment(peer->dstate, &changes[i].add.htlc.rhash); - if (payment) { - if (changes[i].add.htlc.msatoshis != payment->msatoshis) { - log_unusual(peer->log, "Got payment for %"PRIu64 - " not %"PRIu64 " satoshi!", - changes[i].add.htlc.msatoshis, - payment->msatoshis); - command_htlc_fail(peer, - changes[i].add.htlc.id); - } else { - log_info(peer->log, - "Immediately resolving HTLC %"PRIu64, - changes[i].add.htlc.id); - command_htlc_fulfill(peer, - changes[i].add.htlc.id, - &payment->r); - } - } - /* FIXME: Otherwise, route. */ + their_htlc_added(peer, &changes[i].add.htlc); break; case HTLC_FULFILL: /* FIXME: resolve_one_htlc(peer, id, preimage); */ diff --git a/daemon/test/test.sh b/daemon/test/test.sh index 2794bb0cd..74e153fb1 100755 --- a/daemon/test/test.sh +++ b/daemon/test/test.sh @@ -358,6 +358,10 @@ $CLI generate 3 check_peerstate lcli1 STATE_NORMAL check_peerstate lcli2 STATE_NORMAL +# We turn off routing failure for the moment. +lcli1 dev-routefail false +lcli2 dev-routefail false + if [ -n "$DIFFERENT_FEES" ]; then # This is 100,000 satoshi, so covers fees. HTLC_AMOUNT=100000000 @@ -731,6 +735,8 @@ B_AMOUNT=$(($B_AMOUNT + $HTLC_AMOUNT * 2)) check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" # Now, use automatic payment redemption +lcli1 dev-routefail true +lcli2 dev-routefail true RHASH3=`lcli2 accept-payment $HTLC_AMOUNT | sed 's/.*"\([0-9a-f]*\)".*/\1/'` lcli1 newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH3 @@ -748,6 +754,9 @@ check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" # Now, failed payment (didn't pay enough) RHASH4=`lcli2 accept-payment $HTLC_AMOUNT | sed 's/.*"\([0-9a-f]*\)".*/\1/'` +# Shouldn't have this already. +if lcli2 getlog | $FGREP 'Short payment for HTLC'; then exit 1; fi + lcli1 newhtlc $ID2 $(($HTLC_AMOUNT - 1)) $EXPIRY $RHASH4 [ ! -n "$MANUALCOMMIT" ] || lcli1 commit $ID2 [ ! -n "$MANUALCOMMIT" ] || lcli2 commit $ID1 @@ -755,7 +764,7 @@ lcli1 newhtlc $ID2 $(($HTLC_AMOUNT - 1)) $EXPIRY $RHASH4 [ ! -n "$MANUALCOMMIT" ] || lcli2 commit $ID1 [ ! -n "$MANUALCOMMIT" ] || lcli1 commit $ID2 -check lcli2 "getlog | $FGREP 'Got payment for '$(($HTLC_AMOUNT - 1))' not '$HTLC_AMOUNT" +check lcli2 "getlog | $FGREP 'Short payment for HTLC'" check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" lcli1 close $ID2