mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 13:25:43 +01:00
channeld_fakenet: don't be as brute-force trying to derive keys.
Keep a proper cache of all possible ones. I think this may be the timeout problem: according to the logs, channeld_fakenet stops responding and thus HTLCs eventually time out. ``` ``` 2024-12-16T23:16:16.4874420Z lightningd-1 2024-12-16T22:45:14.068Z UNUSUAL 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: Adding HTLC 18446744073709551615 too slow: killing connection ``` Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
4b283eb96e
commit
69bfa6f5b1
@ -41,13 +41,32 @@
|
||||
#define PEER_FD 3
|
||||
#define HSM_FD 4
|
||||
|
||||
/* Map public keys to the private key */
|
||||
struct node {
|
||||
struct privkey p;
|
||||
struct node_id id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const struct node_id *node_key(const struct node *n)
|
||||
{
|
||||
return &n->id;
|
||||
}
|
||||
static bool node_cmp(const struct node *n, const struct node_id *node_id)
|
||||
{
|
||||
return node_id_eq(&n->id, node_id);
|
||||
}
|
||||
HTABLE_DEFINE_TYPE(struct node, node_key, node_id_hash, node_cmp, node_map);
|
||||
|
||||
struct info {
|
||||
/* To talk to lightningd */
|
||||
struct daemon_conn *dc;
|
||||
/* The actual channel (to make sure we can fit!) */
|
||||
struct channel *channel;
|
||||
/* Cache of privkeys which have proven useful */
|
||||
size_t *cached_node_idx;
|
||||
/* Peer's privkey */
|
||||
struct node *peer;
|
||||
/* Fast lookup for node ids to get privkeys */
|
||||
struct node_map *node_map;
|
||||
/* Gossip map for lookup up our "channels" */
|
||||
struct gossmap *gossmap;
|
||||
/* To check cltv delays */
|
||||
@ -72,7 +91,7 @@ struct info {
|
||||
};
|
||||
|
||||
/* FIXME: For the ecdh() function called by onion routines */
|
||||
static size_t current_nodeidx;
|
||||
static struct node *current_node;
|
||||
|
||||
/* Core of an outgoing HTLC: freed by succeed() or fail() */
|
||||
struct fake_htlc {
|
||||
@ -106,40 +125,6 @@ struct reservation {
|
||||
struct amount_msat amount;
|
||||
};
|
||||
|
||||
static void make_privkey(size_t idx, struct privkey *pk)
|
||||
{
|
||||
/* pyln-testing uses 'lightning-N' then all zeroes as hsm_secret. */
|
||||
if (idx & 1) {
|
||||
u32 salt = 0;
|
||||
struct secret hsm_secret;
|
||||
memset(&hsm_secret, 0, sizeof(hsm_secret));
|
||||
snprintf((char *)&hsm_secret, sizeof(hsm_secret),
|
||||
"lightning-%zu", idx >> 1);
|
||||
|
||||
/* This maps hsm_secret -> node privkey */
|
||||
hkdf_sha256(pk, sizeof(*pk),
|
||||
&salt, sizeof(salt),
|
||||
&hsm_secret, sizeof(hsm_secret),
|
||||
"nodeid", 6);
|
||||
return;
|
||||
}
|
||||
|
||||
/* gossmap-compress uses the node index (size_t, native endian), then all ones */
|
||||
memset(pk, 1, sizeof(*pk));
|
||||
idx >>= 1;
|
||||
memcpy(pk, &idx, sizeof(idx));
|
||||
|
||||
struct pubkey pubkey;
|
||||
pubkey_from_privkey(pk, &pubkey);
|
||||
}
|
||||
|
||||
static const char *fmt_nodeidx(const tal_t *ctx, size_t idx)
|
||||
{
|
||||
if (idx & 1)
|
||||
return tal_fmt(ctx, "lightningd-%zu", idx >> 1);
|
||||
return tal_fmt(ctx, "gossmap-node-%zu", idx >> 1);
|
||||
}
|
||||
|
||||
/* Return deterministic value >= min < max for this channel */
|
||||
static u64 channel_range(const struct info *info,
|
||||
const struct short_channel_id_dir *scidd,
|
||||
@ -150,10 +135,8 @@ static u64 channel_range(const struct info *info,
|
||||
|
||||
void ecdh(const struct pubkey *point, struct secret *ss)
|
||||
{
|
||||
struct privkey pk;
|
||||
make_privkey(current_nodeidx, &pk);
|
||||
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
|
||||
pk.secret.data, NULL, NULL) != 1)
|
||||
current_node->p.secret.data, NULL, NULL) != 1)
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -270,7 +253,58 @@ static u8 *get_next_onion(const tal_t *ctx, const struct route_step *rs)
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Sets current_nodeidx, *next_onion_packet, *shared_secret and *me, and decodes */
|
||||
static struct node *make_peer_node(const tal_t *ctx)
|
||||
{
|
||||
struct node *n = tal(ctx, struct node);
|
||||
u32 salt = 0;
|
||||
struct secret hsm_secret;
|
||||
struct pubkey pubkey;
|
||||
|
||||
memset(&hsm_secret, 0, sizeof(hsm_secret));
|
||||
snprintf((char *)&hsm_secret, sizeof(hsm_secret),
|
||||
"lightning-2");
|
||||
|
||||
/* This maps hsm_secret -> node privkey */
|
||||
hkdf_sha256(&n->p, sizeof(n->p),
|
||||
&salt, sizeof(salt),
|
||||
&hsm_secret, sizeof(hsm_secret),
|
||||
"nodeid", 6);
|
||||
pubkey_from_privkey(&n->p, &pubkey);
|
||||
node_id_from_pubkey(&n->id, &pubkey);
|
||||
n->name = tal_fmt(n, "lightningd-2");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* expected_id is NULL for initial node (aka l2) */
|
||||
static struct node *get_current_node(struct info *info,
|
||||
const struct node_id *expected_id)
|
||||
{
|
||||
if (!expected_id)
|
||||
return info->peer;
|
||||
|
||||
return node_map_get(info->node_map, expected_id);
|
||||
}
|
||||
|
||||
static void populate_node_map(const struct gossmap *gossmap,
|
||||
struct node_map *node_map)
|
||||
{
|
||||
for (size_t i = 0; i < gossmap_max_node_idx(gossmap); i++) {
|
||||
struct node *n = tal(node_map, struct node);
|
||||
struct pubkey pubkey;
|
||||
|
||||
/* gossmap-compress uses the node index (size_t, native endian), then all ones */
|
||||
memset(&n->p, 1, sizeof(n->p));
|
||||
memcpy(&n->p, &i, sizeof(i));
|
||||
n->name = tal_fmt(n, "node#%zu", i);
|
||||
|
||||
pubkey_from_privkey(&n->p, &pubkey);
|
||||
node_id_from_pubkey(&n->id, &pubkey);
|
||||
node_map_add(node_map, n);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets current_node, *next_onion_packet, *shared_secret and *me, and decodes */
|
||||
static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
struct info *info,
|
||||
const u8 onion_routing_packet[],
|
||||
@ -278,7 +312,6 @@ static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
const struct sha256 *payment_hash,
|
||||
struct amount_msat amount,
|
||||
u32 cltv,
|
||||
const struct node_id *expected_id,
|
||||
u8 **next_onion_packet,
|
||||
struct secret *shared_secret,
|
||||
struct gossmap_node **me)
|
||||
@ -289,9 +322,6 @@ static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
struct onion_payload *payload;
|
||||
u64 failtlvtype;
|
||||
size_t failtlvpos;
|
||||
struct privkey pk;
|
||||
struct pubkey current_pubkey;
|
||||
struct node_id current_node_id;
|
||||
const char *explanation;
|
||||
|
||||
op = parse_onionpacket(tmpctx, onion_routing_packet,
|
||||
@ -301,35 +331,13 @@ static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Could not parse onion (failcode %u)", failcode);
|
||||
|
||||
/* Try previously-useful keys first */
|
||||
for (size_t i = 0; i < tal_count(info->cached_node_idx); i++) {
|
||||
current_nodeidx = info->cached_node_idx[i];
|
||||
if (!ecdh_maybe_blinding(&op->ephemeralkey, path_key, shared_secret))
|
||||
abort();
|
||||
rs = process_onionpacket(tmpctx, op, shared_secret,
|
||||
payment_hash->u.u8, sizeof(*payment_hash));
|
||||
if (rs)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rs) {
|
||||
/* Try a new one */
|
||||
for (current_nodeidx = 0; current_nodeidx < 100000; current_nodeidx++) {
|
||||
if (!ecdh_maybe_blinding(&op->ephemeralkey, path_key, shared_secret))
|
||||
abort();
|
||||
rs = process_onionpacket(tmpctx, op, shared_secret,
|
||||
payment_hash->u.u8, sizeof(*payment_hash));
|
||||
if (rs)
|
||||
break;
|
||||
}
|
||||
if (!rs)
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Could not find privkey for onion");
|
||||
|
||||
/* Add to cache */
|
||||
tal_arr_expand(&info->cached_node_idx, current_nodeidx);
|
||||
}
|
||||
|
||||
if (!ecdh_maybe_blinding(&op->ephemeralkey, path_key, shared_secret))
|
||||
abort();
|
||||
rs = process_onionpacket(tmpctx, op, shared_secret,
|
||||
payment_hash->u.u8, sizeof(*payment_hash));
|
||||
if (!rs)
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Could not decode onion for %s", current_node->name);
|
||||
*next_onion_packet = get_next_onion(ctx, rs);
|
||||
|
||||
payload = onion_decode(tmpctx,
|
||||
@ -344,26 +352,14 @@ static struct onion_payload *decode_onion(const tal_t *ctx,
|
||||
}
|
||||
|
||||
/* Find ourselves in the gossmap, so we know our channels */
|
||||
make_privkey(current_nodeidx, &pk);
|
||||
pubkey_from_privkey(&pk, ¤t_pubkey);
|
||||
node_id_from_pubkey(¤t_node_id, ¤t_pubkey);
|
||||
|
||||
/* This means pay plugin messed up! */
|
||||
if (expected_id && !node_id_eq(expected_id, ¤t_node_id))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Onion sent to %s, but encrypted to %s",
|
||||
fmt_node_id(tmpctx, expected_id),
|
||||
fmt_node_id(tmpctx, ¤t_node_id));
|
||||
|
||||
*me = gossmap_find_node(info->gossmap, ¤t_node_id);
|
||||
*me = gossmap_find_node(info->gossmap, ¤t_node->id);
|
||||
if (!*me)
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Cannot find %s (%s) in gossmap",
|
||||
fmt_nodeidx(tmpctx, current_nodeidx),
|
||||
fmt_node_id(tmpctx, ¤t_node_id));
|
||||
current_node->name,
|
||||
fmt_node_id(tmpctx, ¤t_node->id));
|
||||
|
||||
status_debug("Unpacked onion for %s",
|
||||
fmt_nodeidx(tmpctx, current_nodeidx));
|
||||
status_debug("Unpacked onion for %s", current_node->name);
|
||||
return payload;
|
||||
}
|
||||
|
||||
@ -382,7 +378,7 @@ static void fail(struct info *info,
|
||||
towire_u16(&msg, failcode);
|
||||
|
||||
status_debug("Failing payment at %s due to %s",
|
||||
fmt_nodeidx(tmpctx, current_nodeidx),
|
||||
fmt_node_id(tmpctx, ¤t_node->id),
|
||||
onion_wire_name(failcode));
|
||||
|
||||
err = channel_fail_htlc(info->channel,
|
||||
@ -573,8 +569,7 @@ static void add_mpp(struct info *info,
|
||||
struct preimage preimage;
|
||||
struct multi_payment *mp;
|
||||
|
||||
status_debug("Received payment at %s",
|
||||
fmt_nodeidx(tmpctx, current_nodeidx));
|
||||
status_debug("Received payment at %s", current_node->name);
|
||||
mp = add_payment_part(info, htlc, payload);
|
||||
if (!mp)
|
||||
return;
|
||||
@ -706,6 +701,13 @@ static void forward_htlc(struct info *info,
|
||||
struct delayed_forward *dfwd;
|
||||
unsigned int msec_delay;
|
||||
|
||||
current_node = get_current_node(info, expected);
|
||||
if (!current_node) {
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Could not find privkey for %s",
|
||||
fmt_node_id(tmpctx, expected));
|
||||
}
|
||||
|
||||
/* Decode, and figure out who I am */
|
||||
payload = decode_onion(tmpctx,
|
||||
info,
|
||||
@ -713,7 +715,6 @@ static void forward_htlc(struct info *info,
|
||||
path_key,
|
||||
&htlc->payment_hash,
|
||||
amount, cltv_expiry,
|
||||
expected,
|
||||
&next_onion_packet,
|
||||
&shared_secret,
|
||||
&me);
|
||||
@ -1291,7 +1292,10 @@ int main(int argc, char *argv[])
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Loading gossmap %s", strerror(errno));
|
||||
|
||||
info->cached_node_idx = tal_arr(info, size_t, 0);
|
||||
info->node_map = tal(info, struct node_map);
|
||||
node_map_init(info->node_map);
|
||||
populate_node_map(info->gossmap, info->node_map);
|
||||
info->peer = make_peer_node(info);
|
||||
info->multi_payments = tal_arr(info, struct multi_payment *, 0);
|
||||
info->reservations = tal_arr(info, struct reservation *, 0);
|
||||
timers_init(&info->timers, time_mono());
|
||||
|
Loading…
Reference in New Issue
Block a user