lightningd: track HTLC ends.

This lets us link HTLCs from one peer to another; but for the moment it
simply means we can adjust balance when an HTLC is fulfilled.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2017-04-01 21:31:13 +10:30
parent 49d97a2fd6
commit 826fb0c2dc
7 changed files with 152 additions and 27 deletions

View File

@ -61,6 +61,7 @@ LIGHTNINGD_SRC := \
lightningd/build_utxos.c \
lightningd/dev_newhtlc.c \
lightningd/gossip_control.c \
lightningd/htlc_end.c \
lightningd/hsm_control.c \
lightningd/lightningd.c \
lightningd/peer_control.c \
@ -76,6 +77,7 @@ LIGHTNINGD_HEADERS_NOGEN = \
lightningd/build_utxos.h \
lightningd/gossip_control.h \
lightningd/hsm_control.h \
lightningd/htlc_end.h \
lightningd/lightningd.h \
lightningd/peer_control.h \
lightningd/subd.h \

View File

@ -3,20 +3,23 @@
#include <daemon/log.h>
#include <daemon/sphinx.h>
#include <lightningd/channel/gen_channel_wire.h>
#include <lightningd/htlc_end.h>
#include <lightningd/lightningd.h>
#include <lightningd/peer_control.h>
#include <lightningd/subd.h>
#include <utils.h>
static bool offer_htlc_reply(struct subd *subd, const u8 *msg, const int *fds,
struct command *cmd)
struct htlc_end *hend)
{
u64 htlc_id;
u16 failcode;
u8 *failstr;
/* We hack this in, since we don't have a real pay_command here. */
struct command *cmd = (void *)hend->pay_command;
if (!fromwire_channel_offer_htlc_reply(msg, msg, NULL,
&htlc_id, &failcode, &failstr)) {
&hend->htlc_id,
&failcode, &failstr)) {
command_fail(cmd, "Invalid reply from daemon: %s",
tal_hex(msg, msg));
return true;
@ -28,8 +31,12 @@ static bool offer_htlc_reply(struct subd *subd, const u8 *msg, const int *fds,
} else {
struct json_result *response = new_json_result(cmd);
/* Peer owns it now (we're about to free cmd) */
tal_steal(hend->peer, hend);
connect_htlc_end(&subd->ld->htlc_ends, hend);
json_object_start(response, NULL);
json_add_u64(response, "id", htlc_id);
json_add_u64(response, "id", hend->htlc_id);
json_object_end(response);
command_success(cmd, response);
}
@ -50,6 +57,7 @@ static void json_dev_newhtlc(struct command *cmd,
u8 sessionkey[32];
struct onionpacket *packet;
u8 *onion;
struct htlc_end *hend;
struct pubkey *path = tal_arrz(cmd, struct pubkey, 1);
if (!json_get_params(buffer, params,
@ -113,9 +121,16 @@ static void json_dev_newhtlc(struct command *cmd,
log_debug(peer->log, "JSON command to add new HTLC");
hend = tal(cmd, struct htlc_end);
hend->which_end = HTLC_DST;
hend->peer = peer;
hend->msatoshis = msatoshi;
hend->other_end = NULL;
hend->pay_command = (void *)cmd;
/* FIXME: If subdaemon dies? */
msg = towire_channel_offer_htlc(cmd, msatoshi, expiry, &rhash, onion);
subd_req(peer, peer->owner, take(msg), -1, 0, offer_htlc_reply, cmd);
subd_req(hend, peer->owner, take(msg), -1, 0, offer_htlc_reply, hend);
}
static const struct json_command dev_newhtlc_command = {

39
lightningd/htlc_end.c Normal file
View File

@ -0,0 +1,39 @@
#include <ccan/cast/cast.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/tal/tal.h>
#include <daemon/pseudorand.h>
#include <lightningd/htlc_end.h>
size_t hash_htlc_end(const struct htlc_end *e)
{
struct siphash24_ctx ctx;
siphash24_init(&ctx, siphash_seed());
/* peer doesn't move while in this hash, so we just hash pointer. */
siphash24_update(&ctx, &e->peer, sizeof(e->peer));
siphash24_u64(&ctx, e->htlc_id);
siphash24_u8(&ctx, e->which_end);
return siphash24_done(&ctx);
}
struct htlc_end *find_htlc_end(const struct htlc_end_map *map,
const struct peer *peer,
u64 htlc_id,
enum htlc_end_type which_end)
{
const struct htlc_end key = { which_end, (struct peer *)peer, htlc_id,
NULL };
return htlc_end_map_get(map, &key);
}
static void remove_htlc_end(struct htlc_end *hend, struct htlc_end_map *map)
{
htlc_end_map_del(map, hend);
}
void connect_htlc_end(struct htlc_end_map *map, struct htlc_end *hend)
{
tal_add_destructor2(hend, remove_htlc_end, map);
htlc_end_map_add(map, hend);
}

47
lightningd/htlc_end.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef LIGHTNING_LIGHTNINGD_HTLC_END_H
#define LIGHTNING_LIGHTNINGD_HTLC_END_H
#include "config.h"
#include <ccan/htable/htable_type.h>
#include <ccan/short_types/short_types.h>
/* A HTLC has a source and destination: if other is NULL, it's this node.
*
* The main daemon simply shuffles them back and forth.
*/
enum htlc_end_type { HTLC_SRC, HTLC_DST };
struct htlc_end {
enum htlc_end_type which_end;
struct peer *peer;
u64 htlc_id;
u64 msatoshis;
struct htlc_end *other_end;
/* If this is driven by a command. */
struct pay_command *pay_command;
};
static inline const struct htlc_end *keyof_htlc_end(const struct htlc_end *e)
{
return e;
}
size_t hash_htlc_end(const struct htlc_end *e);
static inline bool htlc_end_eq(const struct htlc_end *a,
const struct htlc_end *b)
{
return a->peer == b->peer
&& a->htlc_id == b->htlc_id
&& a->which_end == b->which_end;
}
HTABLE_DEFINE_TYPE(struct htlc_end, keyof_htlc_end, hash_htlc_end, htlc_end_eq,
htlc_end_map);
struct htlc_end *find_htlc_end(const struct htlc_end_map *map,
const struct peer *peer,
u64 htlc_id,
enum htlc_end_type which_end);
void connect_htlc_end(struct htlc_end_map *map, struct htlc_end *hend);
#endif /* LIGHTNING_LIGHTNINGD_HTLC_END_H */

View File

@ -110,6 +110,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->bip32_max_index = 0;
ld->dev_debug_subdaemon = NULL;
list_head_init(&ld->utxos);
htlc_end_map_init(&ld->htlc_ends);
ld->dstate.log_book = new_log_book(&ld->dstate, 20*1024*1024,LOG_INFORM);
ld->log = ld->dstate.base_log = new_log(&ld->dstate,
ld->dstate.log_book,

View File

@ -4,6 +4,7 @@
#include <bitcoin/privkey.h>
#include <ccan/container_of/container_of.h>
#include <daemon/lightningd.h>
#include <lightningd/htlc_end.h>
/* BOLT #1:
*
@ -51,6 +52,9 @@ struct lightningd {
/* UTXOs we have available to spend. */
struct list_head utxos;
/* HTLCs in flight. */
struct htlc_end_map htlc_ends;
};
void derive_peer_seed(struct lightningd *ld, struct privkey *peer_seed,

View File

@ -642,18 +642,17 @@ static void fail_htlc(struct peer *peer, u64 htlc_id, enum onion_type failcode)
/* FIXME: implement */
}
static void handle_localpay(struct peer *peer,
u64 htlc_id,
u32 amount_msat,
static void handle_localpay(struct htlc_end *hend,
u32 cltv_expiry,
const struct sha256 *payment_hash)
{
u8 *msg;
struct invoice *invoice = find_unpaid(peer->ld->dstate.invoices,
struct invoice *invoice = find_unpaid(hend->peer->ld->dstate.invoices,
payment_hash);
if (!invoice) {
fail_htlc(peer, htlc_id, WIRE_UNKNOWN_PAYMENT_HASH);
fail_htlc(hend->peer, hend->htlc_id, WIRE_UNKNOWN_PAYMENT_HASH);
tal_free(hend);
return;
}
@ -665,11 +664,13 @@ static void handle_localpay(struct peer *peer,
*
* 1. type: PERM|16 (`incorrect_payment_amount`)
*/
if (amount_msat < invoice->msatoshi) {
fail_htlc(peer, htlc_id, WIRE_INCORRECT_PAYMENT_AMOUNT);
if (hend->msatoshis < invoice->msatoshi) {
fail_htlc(hend->peer, hend->htlc_id,
WIRE_INCORRECT_PAYMENT_AMOUNT);
return;
} else if (amount_msat > invoice->msatoshi * 2) {
fail_htlc(peer, htlc_id, WIRE_INCORRECT_PAYMENT_AMOUNT);
} else if (hend->msatoshis > invoice->msatoshi * 2) {
fail_htlc(hend->peer, hend->htlc_id,
WIRE_INCORRECT_PAYMENT_AMOUNT);
return;
}
@ -677,24 +678,32 @@ static void handle_localpay(struct peer *peer,
*
* If the `cltv-expiry` is too low, the final node MUST fail the HTLC:
*/
if (get_block_height(peer->ld->topology)
+ peer->ld->dstate.config.deadline_blocks >= cltv_expiry) {
log_debug(peer->log, "Expiry cltv %u too close to current %u + deadline %u",
cltv_expiry, get_block_height(peer->ld->topology),
peer->ld->dstate.config.deadline_blocks);
fail_htlc(peer, htlc_id, WIRE_FINAL_EXPIRY_TOO_SOON);
if (get_block_height(hend->peer->ld->topology)
+ hend->peer->ld->dstate.config.deadline_blocks >= cltv_expiry) {
log_debug(hend->peer->log,
"Expiry cltv %u too close to current %u + deadline %u",
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);
tal_free(hend);
return;
}
/* FIXME: fail the peer if it doesn't tell us that htlc fulfill is
* committed before deadline.
*/
log_info(peer->ld->log, "Resolving invoice '%s' with HTLC %"PRIu64,
invoice->label, htlc_id);
connect_htlc_end(&hend->peer->ld->htlc_ends, hend);
msg = towire_channel_fulfill_htlc(peer, htlc_id, &invoice->r);
subd_send_msg(peer->owner, take(msg));
resolve_invoice(&peer->ld->dstate, invoice);
log_info(hend->peer->ld->log, "Resolving invoice '%s' with HTLC %"PRIu64,
invoice->label, hend->htlc_id);
hend->peer->balance[LOCAL] += hend->msatoshis;
hend->peer->balance[REMOTE] -= hend->msatoshis;
msg = towire_channel_fulfill_htlc(hend->peer, hend->htlc_id, &invoice->r);
subd_send_msg(hend->peer->owner, take(msg));
resolve_invoice(&hend->peer->ld->dstate, invoice);
}
static int peer_accepted_htlc(struct peer *peer, const u8 *msg)
@ -706,6 +715,7 @@ static int peer_accepted_htlc(struct peer *peer, const u8 *msg)
bool forward;
u64 amt_to_forward;
u32 outgoing_cltv_value;
struct htlc_end *hend;
if (!fromwire_channel_accepted_htlc(msg, NULL, &id, &amount_msat,
&cltv_expiry, &payment_hash,
@ -717,11 +727,18 @@ static int peer_accepted_htlc(struct peer *peer, const u8 *msg)
return -1;
}
hend = tal(peer, struct htlc_end);
hend->which_end = HTLC_SRC;
hend->peer = peer;
hend->htlc_id = id;
hend->other_end = NULL;
hend->pay_command = NULL;
hend->msatoshis = amount_msat;
if (forward)
log_broken(peer->log, "FIXME: Implement forwarding!");
else
handle_localpay(peer, id,
amount_msat, cltv_expiry, &payment_hash);
handle_localpay(hend, cltv_expiry, &payment_hash);
return 0;
}