mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
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:
parent
49d97a2fd6
commit
826fb0c2dc
@ -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 \
|
||||
|
@ -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
39
lightningd/htlc_end.c
Normal 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
47
lightningd/htlc_end.h
Normal 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 */
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user