mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-24 15:10:51 +01:00
Note the impedence mismatch between sendpay and getroutes: we have to shift amounts and delays by 1. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1142 lines
33 KiB
C
1142 lines
33 KiB
C
/* This is a fake channeld: it doesn't talk to the peer at all, it just
|
|
* pretends to. It knows secret keys though, for a fake network, and can
|
|
* respond as if we are connected to that network.
|
|
*
|
|
* Example known payment_hashes (0000000..., 0100000..., 020000..., ... FF0000...):
|
|
* 66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925
|
|
* 01d0fabd251fcbbe2b93b4b927b26ad2a1a99077152e45ded1e678afa45dbec5
|
|
* 5778f985db754c6628691f56fadae50c65fddbe8eb2e93039633fefa05d45e31
|
|
* 91d3827f052f5a4b44d5fe2bed657c752247365d94f80a33cb09c1436a16b125
|
|
* 4b78063b9c224da311bd1d3fb969bba19e7e91ee07b506f9c4c438828915563f
|
|
* aae761377f3b4f1f07d982783b902314b61a9cbe6ccfdfa96559039f07e332ed
|
|
*/
|
|
#include "config.h"
|
|
#include <bitcoin/script.h>
|
|
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
|
|
#include <ccan/htable/htable_type.h>
|
|
#include <ccan/mem/mem.h>
|
|
#include <ccan/tal/str/str.h>
|
|
#include <channeld/channeld_wiregen.h>
|
|
#include <channeld/full_channel.h>
|
|
#include <common/blinding.h>
|
|
#include <common/daemon_conn.h>
|
|
#include <common/derive_basepoints.h>
|
|
#include <common/ecdh.h>
|
|
#include <common/gossmap.h>
|
|
#include <common/onion_decode.h>
|
|
#include <common/status.h>
|
|
#include <common/subdaemon.h>
|
|
#include <common/timeout.h>
|
|
#include <common/wire_error.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <secp256k1_ecdh.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <wire/wire_sync.h>
|
|
|
|
/* stdin == requests, 3 == peer, 4 = HSM */
|
|
#define MASTER_FD STDIN_FILENO
|
|
#define PEER_FD 3
|
|
#define HSM_FD 4
|
|
|
|
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;
|
|
/* Gossip map for lookup up our "channels" */
|
|
struct gossmap *gossmap;
|
|
/* To check cltv delays */
|
|
u32 current_block_height;
|
|
/* lightningd wants these when we tell it about a "new commitment" */
|
|
size_t commit_num;
|
|
/* MPP parts we've gathered */
|
|
struct multi_payment **multi_payments;
|
|
/* For MPP timers */
|
|
struct timers timers;
|
|
|
|
/* Fake stuff we feed into lightningd */
|
|
struct fee_states *fee_states;
|
|
struct height_states *blockheight_states;
|
|
struct bitcoin_tx *commit_tx;
|
|
struct bitcoin_signature fakesig;
|
|
struct sha256 peer_shaseed;
|
|
};
|
|
|
|
/* FIXME: For the ecdh() function called by onion routines */
|
|
static size_t current_nodeidx;
|
|
|
|
/* Core of an outgoing HTLC: freed by succeed() or fail() */
|
|
struct fake_htlc {
|
|
/* The HTLC id we assigned this */
|
|
u64 htlc_id;
|
|
/* The payment hash */
|
|
struct sha256 payment_hash;
|
|
/* The shared secrets from each decryption */
|
|
struct secret *secrets;
|
|
};
|
|
|
|
struct payment {
|
|
struct fake_htlc *htlc;
|
|
/* onion payload at the final hop */
|
|
const struct onion_payload *payload;
|
|
};
|
|
|
|
/* To re-combine MPP */
|
|
struct multi_payment {
|
|
/* For use in the timeout timer */
|
|
struct info *info;
|
|
/* The key to collect payments by */
|
|
struct sha256 payment_hash;
|
|
/* All the payloads we've gathered */
|
|
struct payment **payments;
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
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)
|
|
abort();
|
|
}
|
|
|
|
static void pretend_got_revoke(struct info *info, u64 htlc_id, enum htlc_state newstate)
|
|
{
|
|
struct changed_htlc *changed;
|
|
struct secret secret;
|
|
struct pubkey next_per_commit_point;
|
|
const u8 *msg;
|
|
|
|
changed = tal_arr(tmpctx, struct changed_htlc, 1);
|
|
changed->id = htlc_id;
|
|
changed->newstate = newstate;
|
|
|
|
if (!per_commit_secret(&info->peer_shaseed,
|
|
&secret,
|
|
info->commit_num - 1))
|
|
abort();
|
|
if (!per_commit_point(&info->peer_shaseed,
|
|
&next_per_commit_point,
|
|
info->commit_num + 1))
|
|
abort();
|
|
|
|
msg = towire_channeld_got_revoke(NULL, info->commit_num - 1, &secret,
|
|
&next_per_commit_point, info->fee_states,
|
|
info->blockheight_states, changed,
|
|
NULL, NULL);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
}
|
|
|
|
/* Tell lightningd that htlc is fully committed. */
|
|
static void update_commitment_tx_added(struct info *info, u64 htlc_id)
|
|
{
|
|
struct changed_htlc *changed;
|
|
u8 *msg;
|
|
|
|
changed = tal_arr(tmpctx, struct changed_htlc, 1);
|
|
changed->id = htlc_id;
|
|
changed->newstate = SENT_ADD_COMMIT;
|
|
/* Tell it we committed */
|
|
msg = towire_channeld_sending_commitsig(NULL,
|
|
info->commit_num,
|
|
NULL,
|
|
info->fee_states,
|
|
info->blockheight_states,
|
|
changed);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
|
|
/* Tell it we got revoke & ack from them. */
|
|
pretend_got_revoke(info, htlc_id, RCVD_ADD_REVOCATION);
|
|
|
|
changed->newstate = RCVD_ADD_ACK_COMMIT;
|
|
msg = towire_channeld_got_commitsig(NULL,
|
|
info->commit_num,
|
|
info->fee_states,
|
|
info->blockheight_states,
|
|
&info->fakesig,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
changed,
|
|
info->commit_tx,
|
|
NULL);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
|
|
/* Final change to SENT_ADD_ACK_REVOCATION is implied */
|
|
info->commit_num++;
|
|
}
|
|
|
|
static bool ecdh_maybe_blinding(const struct pubkey *ephemeral_key,
|
|
const struct pubkey *blinding,
|
|
struct secret *ss)
|
|
{
|
|
struct pubkey point = *ephemeral_key;
|
|
|
|
if (blinding) {
|
|
struct secret hmac;
|
|
struct secret blinding_ss;
|
|
|
|
ecdh(blinding, &blinding_ss);
|
|
/* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */
|
|
subkey_from_hmac("blinded_node_id", &blinding_ss, &hmac);
|
|
|
|
/* We instead tweak the *ephemeral* key from the onion and use
|
|
* our normal privkey: since hsmd knows only how to ECDH with
|
|
* our real key */
|
|
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx,
|
|
&point.pubkey,
|
|
hmac.data) != 1) {
|
|
return false;
|
|
}
|
|
}
|
|
ecdh(&point, ss);
|
|
return true;
|
|
}
|
|
|
|
static u8 *get_next_onion(const tal_t *ctx, const struct route_step *rs)
|
|
{
|
|
switch (rs->nextcase) {
|
|
case ONION_END:
|
|
return NULL;
|
|
case ONION_FORWARD:
|
|
return serialize_onionpacket(ctx, rs->next);
|
|
}
|
|
abort();
|
|
}
|
|
|
|
/* Sets current_nodeidx, *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[],
|
|
const struct pubkey *path_key,
|
|
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)
|
|
{
|
|
struct onionpacket *op;
|
|
enum onion_wire failcode;
|
|
struct route_step *rs = NULL;
|
|
struct onion_payload *payload;
|
|
u64 failtlvtype;
|
|
size_t failtlvpos;
|
|
struct privkey pk;
|
|
struct pubkey current_pubkey;
|
|
struct node_id current_node_id;
|
|
|
|
op = parse_onionpacket(tmpctx, onion_routing_packet,
|
|
TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE),
|
|
&failcode);
|
|
if (!op)
|
|
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);
|
|
}
|
|
|
|
*next_onion_packet = get_next_onion(ctx, rs);
|
|
|
|
payload = onion_decode(tmpctx,
|
|
rs, path_key,
|
|
NULL,
|
|
amount,
|
|
cltv, &failtlvtype, &failtlvpos);
|
|
if (!payload) {
|
|
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
|
"Failed tlvtype %"PRIu64" at %zu",
|
|
failtlvtype, failtlvpos);
|
|
}
|
|
|
|
/* 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);
|
|
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));
|
|
|
|
status_debug("Unpacked onion for %s",
|
|
fmt_nodeidx(tmpctx, current_nodeidx));
|
|
return payload;
|
|
}
|
|
|
|
static void fail(struct info *info,
|
|
struct fake_htlc *htlc STEALS,
|
|
const struct onion_payload *payload,
|
|
enum onion_wire failcode)
|
|
{
|
|
struct failed_htlc *failed;
|
|
const struct failed_htlc **failed_arr;
|
|
u8 *msg;
|
|
struct changed_htlc *changed;
|
|
|
|
msg = tal_arr(tmpctx, u8, 0);
|
|
towire_u16(&msg, failcode);
|
|
|
|
status_debug("Failing payment at %s due to %s",
|
|
fmt_nodeidx(tmpctx, current_nodeidx),
|
|
onion_wire_name(failcode));
|
|
|
|
failed_arr = tal_arr(tmpctx, const struct failed_htlc *, 1);
|
|
failed_arr[0] = failed = tal(failed_arr, struct failed_htlc);
|
|
failed->id = htlc->htlc_id;
|
|
failed->sha256_of_onion = NULL;
|
|
failed->onion = create_onionreply(failed, &htlc->secrets[tal_count(htlc->secrets) - 1], msg);
|
|
|
|
/* We create backwards, using shared secrets to wrap */
|
|
for (int i = tal_count(htlc->secrets) - 1; i >= 0; i--)
|
|
failed->onion = wrap_onionreply(failed, &htlc->secrets[i], failed->onion);
|
|
|
|
msg = towire_channeld_got_commitsig(NULL,
|
|
info->commit_num,
|
|
info->fee_states,
|
|
info->blockheight_states,
|
|
&info->fakesig,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
failed_arr,
|
|
NULL,
|
|
info->commit_tx,
|
|
NULL);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
|
|
changed = tal_arr(tmpctx, struct changed_htlc, 1);
|
|
changed->id = htlc->htlc_id;
|
|
changed->newstate = SENT_REMOVE_ACK_COMMIT;
|
|
|
|
/* Tell it we committed, too */
|
|
msg = towire_channeld_sending_commitsig(NULL,
|
|
info->commit_num,
|
|
NULL,
|
|
info->fee_states,
|
|
info->blockheight_states,
|
|
changed);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
|
|
/* Tell it we got revoke & ack from them. */
|
|
pretend_got_revoke(info, htlc->htlc_id, RCVD_REMOVE_ACK_REVOCATION);
|
|
info->commit_num++;
|
|
tal_free(htlc);
|
|
}
|
|
|
|
static void destroy_multi_payment(struct multi_payment *mp)
|
|
{
|
|
for (size_t i = 0; i < tal_count(mp->info->multi_payments); i++) {
|
|
if (mp->info->multi_payments[i] == mp) {
|
|
tal_arr_remove(&mp->info->multi_payments, i);
|
|
return;
|
|
}
|
|
}
|
|
abort();
|
|
}
|
|
|
|
static void multi_payment_timeout(struct multi_payment *mp)
|
|
{
|
|
for (size_t i = 0; i < tal_count(mp->payments); i++) {
|
|
struct payment *p = mp->payments[i];
|
|
fail(mp->info, p->htlc, p->payload, WIRE_MPP_TIMEOUT);
|
|
}
|
|
tal_free(mp);
|
|
}
|
|
|
|
static struct multi_payment *add_payment_part(struct info *info,
|
|
struct fake_htlc *htlc STEALS,
|
|
const struct onion_payload *payload STEALS)
|
|
{
|
|
struct multi_payment *mp;
|
|
struct payment *p;
|
|
struct amount_msat total;
|
|
|
|
for (size_t i = 0; i < tal_count(info->multi_payments); i++) {
|
|
mp = info->multi_payments[i];
|
|
if (sha256_eq(&mp->payment_hash, &htlc->payment_hash)) {
|
|
/* Cannot change total! */
|
|
assert(amount_msat_eq(*mp->payments[0]->payload->total_msat,
|
|
*payload->total_msat));
|
|
goto found;
|
|
}
|
|
}
|
|
mp = tal(info->multi_payments, struct multi_payment);
|
|
mp->payment_hash = htlc->payment_hash;
|
|
mp->payments = tal_arr(mp, struct payment *, 0);
|
|
mp->info = info;
|
|
tal_arr_expand(&info->multi_payments, mp);
|
|
tal_add_destructor(mp, destroy_multi_payment);
|
|
|
|
/* BOLT #4:
|
|
* - MUST fail all HTLCs in the HTLC set after some reasonable timeout.
|
|
* - SHOULD wait for at least 60 seconds after the initial HTLC.
|
|
* - SHOULD use `mpp_timeout` for the failure message.
|
|
*/
|
|
new_reltimer(&info->timers,
|
|
mp,
|
|
time_from_sec(60),
|
|
multi_payment_timeout, mp);
|
|
|
|
found:
|
|
p = tal(mp, struct payment);
|
|
p->htlc = tal_steal(p, htlc);
|
|
p->payload = tal_steal(p, payload);
|
|
tal_arr_expand(&mp->payments, p);
|
|
|
|
/* If amount is enough, return it */
|
|
total = AMOUNT_MSAT(0);
|
|
for (size_t i = 0; i < tal_count(mp->payments); i++) {
|
|
p = mp->payments[i];
|
|
if (!amount_msat_accumulate(&total,
|
|
p->payload->amt_to_forward))
|
|
abort();
|
|
}
|
|
|
|
if (amount_msat_less(total, *mp->payments[0]->payload->total_msat))
|
|
return NULL;
|
|
|
|
/* Done! */
|
|
return mp;
|
|
}
|
|
|
|
static void succeed(struct info *info,
|
|
struct fake_htlc *htlc STEALS,
|
|
const struct onion_payload *payload,
|
|
const struct preimage *preimage)
|
|
{
|
|
struct changed_htlc *changed;
|
|
struct fulfilled_htlc *fulfilled;
|
|
u8 *msg;
|
|
|
|
fulfilled = tal_arr(tmpctx, struct fulfilled_htlc, 1);
|
|
fulfilled->id = htlc->htlc_id;
|
|
fulfilled->payment_preimage = *preimage;
|
|
|
|
msg = towire_channeld_got_commitsig(NULL,
|
|
info->commit_num,
|
|
info->fee_states,
|
|
info->blockheight_states,
|
|
&info->fakesig,
|
|
NULL,
|
|
NULL,
|
|
fulfilled,
|
|
NULL,
|
|
NULL,
|
|
info->commit_tx,
|
|
NULL);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
|
|
changed = tal_arr(tmpctx, struct changed_htlc, 1);
|
|
changed->id = htlc->htlc_id;
|
|
changed->newstate = SENT_REMOVE_ACK_COMMIT;
|
|
|
|
/* Tell it we committed, too */
|
|
msg = towire_channeld_sending_commitsig(NULL,
|
|
info->commit_num,
|
|
NULL,
|
|
info->fee_states,
|
|
info->blockheight_states,
|
|
changed);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
|
|
/* Tell it we got revoke & ack from them. */
|
|
pretend_got_revoke(info, htlc->htlc_id, RCVD_REMOVE_ACK_REVOCATION);
|
|
|
|
info->commit_num++;
|
|
tal_free(htlc);
|
|
}
|
|
|
|
static void add_mpp(struct info *info,
|
|
struct fake_htlc *htlc STEALS,
|
|
const struct onion_payload *payload STEALS)
|
|
{
|
|
struct preimage preimage;
|
|
struct multi_payment *mp;
|
|
|
|
status_debug("Received payment at %s",
|
|
fmt_nodeidx(tmpctx, current_nodeidx));
|
|
mp = add_payment_part(info, htlc, payload);
|
|
if (!mp)
|
|
return;
|
|
|
|
/* Completed payment. Guess payment_hash */
|
|
memset(&preimage, 0, sizeof(preimage));
|
|
for (size_t n = 0; n < 256; n++) {
|
|
struct sha256 hash;
|
|
preimage.r[0] = n;
|
|
sha256(&hash, &preimage, sizeof(preimage));
|
|
if (sha256_eq(&hash, &mp->payment_hash)) {
|
|
for (size_t i = 0; i < tal_count(mp->payments); i++) {
|
|
struct payment *p = mp->payments[i];
|
|
succeed(info, p->htlc, p->payload, &preimage);
|
|
}
|
|
tal_free(mp);
|
|
return;
|
|
}
|
|
status_debug("payment_hash %zu = %s",
|
|
n, fmt_sha256(tmpctx, &hash));
|
|
}
|
|
|
|
/* Unknown, fail them all. */
|
|
for (size_t i = 0; i < tal_count(mp->payments); i++) {
|
|
struct payment *p = mp->payments[i];
|
|
fail(info, p->htlc, p->payload,
|
|
WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS);
|
|
}
|
|
tal_free(mp);
|
|
}
|
|
|
|
static void forward_htlc(struct info *info,
|
|
struct fake_htlc *htlc,
|
|
struct amount_msat amount,
|
|
u32 cltv_expiry,
|
|
const u8 onion_routing_packet[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)],
|
|
const struct pubkey *path_key,
|
|
const struct node_id *expected)
|
|
{
|
|
struct onion_payload *payload;
|
|
u8 *next_onion_packet;
|
|
struct gossmap_node *me;
|
|
struct secret shared_secret;
|
|
struct node_id next;
|
|
struct gossmap_chan *c;
|
|
struct short_channel_id_dir scidd;
|
|
struct amount_msat amt_expected, htlc_min, htlc_max;
|
|
struct pubkey *next_path_key;
|
|
|
|
/* Decode, and figure out who I am */
|
|
payload = decode_onion(tmpctx,
|
|
info,
|
|
onion_routing_packet,
|
|
path_key,
|
|
&htlc->payment_hash,
|
|
amount, cltv_expiry,
|
|
expected,
|
|
&next_onion_packet,
|
|
&shared_secret,
|
|
&me);
|
|
|
|
tal_arr_expand(&htlc->secrets, shared_secret);
|
|
if (!next_onion_packet) {
|
|
if (cltv_expiry < payload->outgoing_cltv) {
|
|
fail(info, htlc, payload,
|
|
WIRE_FINAL_INCORRECT_CLTV_EXPIRY);
|
|
return;
|
|
}
|
|
if (cltv_expiry < info->current_block_height + 18) {
|
|
fail(info, htlc, payload,
|
|
WIRE_FINAL_INCORRECT_CLTV_EXPIRY);
|
|
return;
|
|
}
|
|
|
|
/* MPP: consume htlc and payload */
|
|
add_mpp(info, htlc, payload);
|
|
return;
|
|
}
|
|
|
|
/* Find next node by channel or scid */
|
|
for (size_t i = 0; i < me->num_chans; i++) {
|
|
c = gossmap_nth_chan(info->gossmap, me, i, &scidd.dir);
|
|
/* Get peer on other end */
|
|
gossmap_node_get_id(info->gossmap,
|
|
gossmap_nth_node(info->gossmap, c,
|
|
!scidd.dir),
|
|
&next);
|
|
scidd.scid = gossmap_chan_scid(info->gossmap, c);
|
|
if (payload->forward_channel) {
|
|
if (short_channel_id_eq(scidd.scid,
|
|
*payload->forward_channel)) {
|
|
goto found_next;
|
|
}
|
|
} else {
|
|
struct node_id fwd;
|
|
node_id_from_pubkey(&fwd, payload->forward_node_id);
|
|
if (node_id_eq(&next, &fwd))
|
|
goto found_next;
|
|
}
|
|
}
|
|
fail(info, htlc, payload, WIRE_UNKNOWN_NEXT_PEER);
|
|
return;
|
|
|
|
found_next:
|
|
/* CLTV delta and fees must be correct */
|
|
if (!gossmap_chan_set(c, scidd.dir)) {
|
|
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
|
"Channel used %s is not set?",
|
|
fmt_short_channel_id_dir(tmpctx, &scidd));
|
|
}
|
|
if (payload->outgoing_cltv + c->half[scidd.dir].delay < cltv_expiry) {
|
|
fail(info, htlc, payload, WIRE_INCORRECT_CLTV_EXPIRY);
|
|
return;
|
|
}
|
|
amt_expected = payload->amt_to_forward;
|
|
if (!amount_msat_add_fee(&amt_expected,
|
|
c->half[scidd.dir].base_fee,
|
|
c->half[scidd.dir].proportional_fee))
|
|
abort();
|
|
if (amount_msat_less(amount, amt_expected)) {
|
|
fail(info, htlc, payload, WIRE_FEE_INSUFFICIENT);
|
|
return;
|
|
}
|
|
|
|
/* Obey our HTLC rules please! */
|
|
gossmap_chan_get_update_details(info->gossmap, c, scidd.dir,
|
|
NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&htlc_min, &htlc_max);
|
|
if (amount_msat_less(payload->amt_to_forward, htlc_min)) {
|
|
status_broken("Amount %s is below minimum (%s) for %s!",
|
|
fmt_amount_msat(tmpctx, payload->amt_to_forward),
|
|
fmt_amount_msat(tmpctx, htlc_min),
|
|
fmt_short_channel_id_dir(tmpctx, &scidd));
|
|
fail(info, htlc, payload, WIRE_AMOUNT_BELOW_MINIMUM);
|
|
return;
|
|
}
|
|
|
|
if (amount_msat_greater(payload->amt_to_forward, htlc_max)) {
|
|
status_broken("Amount %s is above maximum (%s) for %s!",
|
|
fmt_amount_msat(tmpctx, payload->amt_to_forward),
|
|
fmt_amount_msat(tmpctx, htlc_max),
|
|
fmt_short_channel_id_dir(tmpctx, &scidd));
|
|
fail(info, htlc, payload, WIRE_TEMPORARY_CHANNEL_FAILURE);
|
|
return;
|
|
}
|
|
|
|
if (payload->path_key) {
|
|
struct sha256 sha;
|
|
blinding_hash_e_and_ss(payload->path_key,
|
|
&payload->blinding_ss,
|
|
&sha);
|
|
next_path_key = tal(tmpctx, struct pubkey);
|
|
blinding_next_path_key(payload->path_key, &sha,
|
|
next_path_key);
|
|
} else
|
|
next_path_key = NULL;
|
|
|
|
/* Limited recursion for the "next node" */
|
|
forward_htlc(info,
|
|
htlc,
|
|
payload->amt_to_forward,
|
|
payload->outgoing_cltv,
|
|
next_onion_packet,
|
|
next_path_key,
|
|
&next);
|
|
}
|
|
|
|
static void handle_offer_htlc(struct info *info, const u8 *inmsg)
|
|
{
|
|
u8 *msg;
|
|
u32 cltv_expiry;
|
|
struct amount_msat amount;
|
|
struct sha256 payment_hash;
|
|
u8 onion_routing_packet[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)];
|
|
enum channel_add_err e;
|
|
const u8 *failwiremsg;
|
|
const char *failstr;
|
|
struct amount_sat htlc_fee;
|
|
struct pubkey *blinding;
|
|
static u64 htlc_id;
|
|
struct fake_htlc *htlc = tal(info, struct fake_htlc);
|
|
|
|
htlc->secrets = tal_arr(htlc, struct secret, 0);
|
|
htlc->htlc_id = htlc_id;
|
|
if (!fromwire_channeld_offer_htlc(tmpctx, inmsg, &amount,
|
|
&cltv_expiry, &htlc->payment_hash,
|
|
onion_routing_packet, &blinding))
|
|
master_badmsg(WIRE_CHANNELD_OFFER_HTLC, inmsg);
|
|
|
|
e = channel_add_htlc(info->channel, LOCAL, htlc->htlc_id,
|
|
amount, cltv_expiry, &payment_hash,
|
|
onion_routing_packet, take(blinding), NULL,
|
|
&htlc_fee, true);
|
|
status_debug("Adding HTLC %"PRIu64" amount=%s cltv=%u gave %s",
|
|
htlc->htlc_id, fmt_amount_msat(tmpctx, amount),
|
|
cltv_expiry,
|
|
channel_add_err_name(e));
|
|
|
|
switch (e) {
|
|
case CHANNEL_ERR_ADD_OK:
|
|
/* Tell lightningd. */
|
|
msg = towire_channeld_offer_htlc_reply(NULL, htlc_id,
|
|
0, "");
|
|
daemon_conn_send(info->dc, take(msg));
|
|
/* Tell it it's locked in */
|
|
update_commitment_tx_added(info, htlc_id);
|
|
|
|
/* Handle it. */
|
|
forward_htlc(info, htlc, amount, cltv_expiry,
|
|
onion_routing_packet, blinding, NULL);
|
|
htlc_id++;
|
|
return;
|
|
case CHANNEL_ERR_INVALID_EXPIRY:
|
|
failwiremsg = towire_incorrect_cltv_expiry(inmsg, cltv_expiry, NULL);
|
|
failstr = tal_fmt(inmsg, "Invalid cltv_expiry %u", cltv_expiry);
|
|
goto failed;
|
|
case CHANNEL_ERR_DUPLICATE:
|
|
case CHANNEL_ERR_DUPLICATE_ID_DIFFERENT:
|
|
status_failed(STATUS_FAIL_MASTER_IO,
|
|
"Duplicate HTLC %"PRIu64, htlc_id);
|
|
|
|
case CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED:
|
|
failwiremsg = towire_required_node_feature_missing(inmsg);
|
|
failstr = "Mini mode: maximum value exceeded";
|
|
goto failed;
|
|
/* FIXME: Fuzz the boundaries a bit to avoid probing? */
|
|
case CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED:
|
|
failwiremsg = towire_temporary_channel_failure(inmsg, NULL);
|
|
failstr = tal_fmt(inmsg, "Capacity exceeded - HTLC fee: %s", fmt_amount_sat(inmsg, htlc_fee));
|
|
goto failed;
|
|
case CHANNEL_ERR_HTLC_BELOW_MINIMUM:
|
|
failwiremsg = towire_amount_below_minimum(inmsg, amount, NULL);
|
|
failstr = tal_fmt(inmsg, "HTLC too small (%s minimum)",
|
|
fmt_amount_msat(tmpctx,
|
|
info->channel->config[REMOTE].htlc_minimum));
|
|
goto failed;
|
|
case CHANNEL_ERR_TOO_MANY_HTLCS:
|
|
failwiremsg = towire_temporary_channel_failure(inmsg, NULL);
|
|
failstr = "Too many HTLCs";
|
|
goto failed;
|
|
case CHANNEL_ERR_DUST_FAILURE:
|
|
/* BOLT-919 #2:
|
|
* - upon an outgoing HTLC:
|
|
* - if a HTLC's `amount_msat` is inferior the counterparty's...
|
|
* - SHOULD NOT send this HTLC
|
|
* - SHOULD fail this HTLC if it's forwarded
|
|
*/
|
|
failwiremsg = towire_temporary_channel_failure(inmsg, NULL);
|
|
failstr = "HTLC too dusty, allowed dust limit reached";
|
|
goto failed;
|
|
}
|
|
/* Shouldn't return anything else! */
|
|
abort();
|
|
|
|
failed:
|
|
/* lightningd appends update to this for us */
|
|
msg = towire_channeld_offer_htlc_reply(NULL, 0, failwiremsg, failstr);
|
|
daemon_conn_send(info->dc, take(msg));
|
|
}
|
|
|
|
static void handle_feerates(struct info *info, const u8 *inmsg)
|
|
{
|
|
u32 feerate, min, max, penalty;
|
|
|
|
if (!fromwire_channeld_feerates(inmsg, &feerate,
|
|
&min, &max, &penalty))
|
|
master_badmsg(WIRE_CHANNELD_FEERATES, inmsg);
|
|
|
|
/* BOLT #2:
|
|
*
|
|
* The node _responsible_ for paying the Bitcoin fee:
|
|
* - SHOULD send `update_fee` to ensure the current fee rate is
|
|
* sufficient (by a significant margin) for timely processing of the
|
|
* commitment transaction.
|
|
*/
|
|
if (info->channel->opener == LOCAL) {
|
|
if (!channel_update_feerate(info->channel, feerate)) {
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handle_blockheight(struct info *info, const u8 *inmsg)
|
|
{
|
|
if (!fromwire_channeld_blockheight(inmsg, &info->current_block_height))
|
|
master_badmsg(WIRE_CHANNELD_BLOCKHEIGHT, inmsg);
|
|
}
|
|
|
|
static void handle_funding_depth(struct info *info, const u8 *inmsg)
|
|
{
|
|
struct short_channel_id *short_channel_id;
|
|
u32 depth;
|
|
bool splicing;
|
|
struct bitcoin_txid txid;
|
|
struct pubkey somepoint;
|
|
|
|
if (!fromwire_channeld_funding_depth(tmpctx, inmsg, &short_channel_id,
|
|
&depth, &splicing, &txid))
|
|
master_badmsg(WIRE_CHANNELD_FUNDING_DEPTH, inmsg);
|
|
|
|
/* Tell it the channel is ready ONCE, so it goes into CHANNELD_NORMAL.
|
|
* We make up the remote_per_commit */
|
|
if (depth != 1)
|
|
return;
|
|
|
|
pubkey_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518",
|
|
strlen("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"),
|
|
&somepoint);
|
|
/* Tell the peer we are ready: it will send a channel_update
|
|
* then to make lightningd happy */
|
|
wire_sync_write(PEER_FD,
|
|
take(towire_channel_ready(NULL,
|
|
&info->channel->cid,
|
|
&somepoint, NULL)));
|
|
|
|
/* Ignore peer msgs except for channel_ready */
|
|
while (fromwire_peektype(wire_sync_read(tmpctx, PEER_FD))
|
|
!= WIRE_CHANNEL_READY);
|
|
|
|
daemon_conn_send(info->dc,
|
|
take(towire_channeld_got_channel_ready(NULL, &somepoint, NULL)));
|
|
}
|
|
|
|
static void handle_dev_peer_shachain(struct info *info, const u8 *msg)
|
|
{
|
|
if (!fromwire_channeld_dev_peer_shachain(msg, &info->peer_shaseed))
|
|
master_badmsg(WIRE_CHANNELD_DEV_PEER_SHACHAIN, msg);
|
|
}
|
|
|
|
/* We don't care, but lightningd expects channeld to respond. */
|
|
static void handle_dev_memleak(struct info *info, const u8 *msg)
|
|
{
|
|
daemon_conn_send(info->dc,
|
|
take(towire_channeld_dev_memleak_reply(NULL, false)));
|
|
}
|
|
|
|
static struct channel *handle_init(struct info *info, const u8 *init_msg)
|
|
{
|
|
struct feature_set *our_features;
|
|
u32 *hsm_capabilities;
|
|
struct channel_id channel_id;
|
|
struct basepoints points[NUM_SIDES];
|
|
struct amount_sat funding_sats;
|
|
struct amount_msat local_msat;
|
|
struct pubkey funding_pubkey[NUM_SIDES];
|
|
struct channel_config conf[NUM_SIDES];
|
|
struct bitcoin_outpoint funding;
|
|
enum side opener;
|
|
struct existing_htlc **htlcs;
|
|
bool reconnected;
|
|
u32 final_index;
|
|
struct ext_key final_ext_key;
|
|
u8 *fwd_msg;
|
|
u32 minimum_depth, lease_expiry;
|
|
struct secret last_remote_per_commit_secret;
|
|
struct penalty_base *pbases;
|
|
bool reestablish_only;
|
|
struct channel_type *channel_type;
|
|
u32 feerate_min, feerate_max, feerate_penalty;
|
|
struct pubkey remote_per_commit;
|
|
struct pubkey old_remote_per_commit;
|
|
u32 commit_msec;
|
|
bool last_was_revoke;
|
|
struct changed_htlc *last_sent_commit;
|
|
u64 revocations_received;
|
|
u8 channel_flags;
|
|
bool channel_ready[NUM_SIDES];
|
|
u64 next_index[NUM_SIDES];
|
|
u64 htlc_id;
|
|
struct bitcoin_signature their_commit_sig;
|
|
struct short_channel_id short_channel_ids[NUM_SIDES];
|
|
bool send_shutdown;
|
|
bool shutdown_sent[NUM_SIDES];
|
|
u8 *final_scriptpubkey;
|
|
u8 *their_features;
|
|
u8 *remote_upfront_shutdown_script;
|
|
bool experimental_upgrade;
|
|
u32 *dev_disable_commit;
|
|
struct inflight **inflights;
|
|
struct short_channel_id local_alias;
|
|
struct channel *channel;
|
|
const u8 *wscript;
|
|
char *err_reason;
|
|
struct wally_tx_output *direct_outputs[NUM_SIDES];
|
|
struct htlc_map *htlc_map;
|
|
|
|
if (!fromwire_channeld_init(info, init_msg,
|
|
&chainparams,
|
|
&our_features,
|
|
&hsm_capabilities,
|
|
&channel_id,
|
|
&funding,
|
|
&funding_sats,
|
|
&minimum_depth,
|
|
&info->current_block_height,
|
|
&info->blockheight_states,
|
|
&lease_expiry,
|
|
&conf[LOCAL], &conf[REMOTE],
|
|
&info->fee_states,
|
|
&feerate_min,
|
|
&feerate_max,
|
|
&feerate_penalty,
|
|
&their_commit_sig,
|
|
&funding_pubkey[REMOTE],
|
|
&points[REMOTE],
|
|
&remote_per_commit,
|
|
&old_remote_per_commit,
|
|
&opener,
|
|
&local_msat,
|
|
&points[LOCAL],
|
|
&funding_pubkey[LOCAL],
|
|
&commit_msec,
|
|
&last_was_revoke,
|
|
&last_sent_commit,
|
|
&next_index[LOCAL],
|
|
&next_index[REMOTE],
|
|
&revocations_received,
|
|
&htlc_id,
|
|
&htlcs,
|
|
&channel_ready[LOCAL],
|
|
&channel_ready[REMOTE],
|
|
&short_channel_ids[LOCAL],
|
|
&reconnected,
|
|
&send_shutdown,
|
|
&shutdown_sent[REMOTE],
|
|
&final_index,
|
|
&final_ext_key,
|
|
&final_scriptpubkey,
|
|
&channel_flags,
|
|
&fwd_msg,
|
|
&last_remote_per_commit_secret,
|
|
&their_features,
|
|
&remote_upfront_shutdown_script,
|
|
&channel_type,
|
|
&dev_disable_commit,
|
|
&pbases,
|
|
&reestablish_only,
|
|
&experimental_upgrade,
|
|
&inflights,
|
|
&local_alias))
|
|
abort();
|
|
|
|
status_debug("Parsed init...");
|
|
channel = new_full_channel(info, &channel_id,
|
|
&funding,
|
|
minimum_depth,
|
|
info->blockheight_states,
|
|
lease_expiry,
|
|
funding_sats,
|
|
local_msat,
|
|
info->fee_states,
|
|
&conf[LOCAL], &conf[REMOTE],
|
|
&points[LOCAL], &points[REMOTE],
|
|
&funding_pubkey[LOCAL],
|
|
&funding_pubkey[REMOTE],
|
|
take(channel_type),
|
|
feature_offered(their_features,
|
|
OPT_LARGE_CHANNELS),
|
|
opener);
|
|
|
|
/* We need a tx, so use this. It gets upset if channel->htlcs
|
|
* is set, so temporarily clear that! */
|
|
htlc_map = channel->htlcs;
|
|
channel->htlcs = NULL;
|
|
info->commit_tx = initial_channel_tx(info, &wscript, channel, &remote_per_commit,
|
|
LOCAL,
|
|
direct_outputs, &err_reason);
|
|
channel->htlcs = htlc_map;
|
|
|
|
status_debug("Created channel");
|
|
daemon_conn_send(info->dc, take(towire_channeld_reestablished(NULL)));
|
|
|
|
return channel;
|
|
}
|
|
|
|
static void master_gone(struct daemon_conn *dc UNUSED)
|
|
{
|
|
daemon_shutdown();
|
|
/* Can't tell master, it's gone. */
|
|
exit(2);
|
|
}
|
|
|
|
static struct io_plan *recv_req(struct io_conn *conn,
|
|
const u8 *msg,
|
|
struct info *info)
|
|
{
|
|
enum channeld_wire t = fromwire_peektype(msg);
|
|
|
|
switch (t) {
|
|
/* We ignore these */
|
|
case WIRE_CHANNELD_SEND_ERROR:
|
|
case WIRE_CHANNELD_SENDING_COMMITSIG_REPLY:
|
|
case WIRE_CHANNELD_GOT_REVOKE_REPLY:
|
|
case WIRE_CHANNELD_GOT_COMMITSIG_REPLY:
|
|
goto out;
|
|
case WIRE_CHANNELD_INIT:
|
|
info->channel = handle_init(info, msg);
|
|
goto out;
|
|
case WIRE_CHANNELD_BLOCKHEIGHT:
|
|
handle_blockheight(info, msg);
|
|
goto out;
|
|
case WIRE_CHANNELD_OFFER_HTLC:
|
|
handle_offer_htlc(info, msg);
|
|
goto out;
|
|
case WIRE_CHANNELD_FEERATES:
|
|
handle_feerates(info, msg);
|
|
goto out;
|
|
case WIRE_CHANNELD_FUNDING_DEPTH:
|
|
handle_funding_depth(info, msg);
|
|
goto out;
|
|
case WIRE_CHANNELD_DEV_MEMLEAK:
|
|
handle_dev_memleak(info, msg);
|
|
goto out;
|
|
case WIRE_CHANNELD_DEV_PEER_SHACHAIN:
|
|
handle_dev_peer_shachain(info, msg);
|
|
goto out;
|
|
/* No incoming HTLCs, these should not happen */
|
|
case WIRE_CHANNELD_FULFILL_HTLC:
|
|
case WIRE_CHANNELD_FAIL_HTLC:
|
|
/* Don't try closing this channel! */
|
|
case WIRE_CHANNELD_SEND_SHUTDOWN:
|
|
/* Don't try to splice */
|
|
case WIRE_CHANNELD_SPLICE_INIT:
|
|
case WIRE_CHANNELD_SPLICE_UPDATE:
|
|
case WIRE_CHANNELD_SPLICE_SIGNED:
|
|
case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT:
|
|
case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED:
|
|
case WIRE_CHANNELD_SPLICE_SENDING_SIGS:
|
|
case WIRE_CHANNELD_SPLICE_CONFIRMED_UPDATE:
|
|
case WIRE_CHANNELD_SPLICE_LOOKUP_TX:
|
|
case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT:
|
|
case WIRE_CHANNELD_SPLICE_FEERATE_ERROR:
|
|
case WIRE_CHANNELD_SPLICE_FUNDING_ERROR:
|
|
case WIRE_CHANNELD_SPLICE_ABORT:
|
|
/* Not supported */
|
|
case WIRE_CHANNELD_DEV_REENABLE_COMMIT:
|
|
case WIRE_CHANNELD_DEV_QUIESCE:
|
|
/* We send these, not receive */
|
|
case WIRE_CHANNELD_OFFER_HTLC_REPLY:
|
|
case WIRE_CHANNELD_SENDING_COMMITSIG:
|
|
case WIRE_CHANNELD_GOT_COMMITSIG:
|
|
case WIRE_CHANNELD_GOT_REVOKE:
|
|
case WIRE_CHANNELD_GOT_CHANNEL_READY:
|
|
case WIRE_CHANNELD_GOT_SPLICE_LOCKED:
|
|
case WIRE_CHANNELD_GOT_ANNOUNCEMENT:
|
|
case WIRE_CHANNELD_GOT_SHUTDOWN:
|
|
case WIRE_CHANNELD_SHUTDOWN_COMPLETE:
|
|
case WIRE_CHANNELD_DEV_REENABLE_COMMIT_REPLY:
|
|
case WIRE_CHANNELD_FAIL_FALLEN_BEHIND:
|
|
case WIRE_CHANNELD_DEV_MEMLEAK_REPLY:
|
|
case WIRE_CHANNELD_SEND_ERROR_REPLY:
|
|
case WIRE_CHANNELD_DEV_QUIESCE_REPLY:
|
|
case WIRE_CHANNELD_UPGRADED:
|
|
case WIRE_CHANNELD_ADD_INFLIGHT:
|
|
case WIRE_CHANNELD_UPDATE_INFLIGHT:
|
|
case WIRE_CHANNELD_GOT_INFLIGHT:
|
|
case WIRE_CHANNELD_SPLICE_STATE_ERROR:
|
|
case WIRE_CHANNELD_LOCAL_ANCHOR_INFO:
|
|
case WIRE_CHANNELD_REESTABLISHED:
|
|
break;
|
|
}
|
|
master_badmsg(-1, msg);
|
|
|
|
out:
|
|
/* Read the next message. */
|
|
return daemon_conn_read_next(conn, info->dc);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct info *info;
|
|
|
|
setup_locale();
|
|
|
|
subdaemon_setup(argc, argv);
|
|
info = tal(NULL, struct info);
|
|
|
|
info->dc = daemon_conn_new(info, MASTER_FD,
|
|
recv_req, NULL, info);
|
|
tal_add_destructor(info->dc, master_gone);
|
|
|
|
status_setup_async(info->dc);
|
|
|
|
info->gossmap = gossmap_load(info, GOSSIP_STORE_FILENAME, NULL);
|
|
if (!info->gossmap)
|
|
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
|
"Loading gossmap %s", strerror(errno));
|
|
|
|
info->cached_node_idx = tal_arr(info, size_t, 0);
|
|
info->multi_payments = tal_arr(info, struct multi_payment *, 0);
|
|
timers_init(&info->timers, time_mono());
|
|
info->commit_num = 1;
|
|
info->fakesig.sighash_type = SIGHASH_ALL;
|
|
memset(&info->fakesig.s, 0, sizeof(info->fakesig.s));
|
|
|
|
status_debug("Waiting for init...");
|
|
|
|
/* This loop never exits. io_loop() only returns if a timer has
|
|
* expired, or io_break() is called, or all fds are closed. We don't
|
|
* use io_break and closing the lightningd fd calls master_gone()
|
|
* which exits. */
|
|
for (;;) {
|
|
struct timer *expired = NULL;
|
|
io_loop(&info->timers, &expired);
|
|
|
|
timer_expired(expired);
|
|
}
|
|
}
|