diff --git a/lightningd/channel.c b/lightningd/channel.c new file mode 100644 index 000000000..99007e796 --- /dev/null +++ b/lightningd/channel.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void destroy_channel(struct channel *channel) +{ + list_del_from(&channel->peer->channels, &channel->list); +} + +/* FIXME: We have no business knowing this! */ +/** + * derive_channel_seed - Generate a unique secret for this peer's channel + * + * @ld: the lightning daemon to get global secret from + * @seed: where to store the generated secret + * @peer_id: the id node_id of the remote peer + * @dbid: channel DBID + * + * This method generates a unique secret from the given parameters. It + * is important that this secret be unique for each channel, but it + * must be reproducible for the same channel in case of + * reconnection. We use the DB channel ID to guarantee unique secrets + * per channel. + */ +void derive_channel_seed(struct lightningd *ld, struct privkey *seed, + const struct pubkey *peer_id, + const u64 dbid) +{ + u8 input[PUBKEY_DER_LEN + sizeof(dbid)]; + char *info = "per-peer seed"; + pubkey_to_der(input, peer_id); + memcpy(input + PUBKEY_DER_LEN, &dbid, sizeof(dbid)); + + assert(dbid != 0); + hkdf_sha256(seed, sizeof(*seed), + input, sizeof(input), + &ld->peer_seed, sizeof(ld->peer_seed), + info, strlen(info)); +} + +struct channel *new_channel(struct peer *peer, u64 dbid, u32 first_blocknum) +{ + /* FIXME: We currently rely on it being all zero/NULL */ + struct channel *channel = talz(peer->ld, struct channel); + + channel->dbid = dbid; + channel->peer = peer; + channel->first_blocknum = first_blocknum; + channel->state = UNINITIALIZED; + channel->local_shutdown_idx = -1; + + /* FIXME: update log prefix when we get scid */ + channel->log = new_log(channel, peer->log_book, "%s chan #%"PRIu64":", + log_prefix(peer->log), dbid); + list_add_tail(&peer->channels, &channel->list); + tal_add_destructor(channel, destroy_channel); + if (channel->dbid != 0) + derive_channel_seed(peer->ld, &channel->seed, &peer->id, + channel->dbid); + + return channel; +} + +const char *channel_state_name(const struct channel *channel) +{ + return peer_state_name(channel->state); +} + +struct channel *peer_active_channel(struct peer *peer) +{ + struct channel *channel; + + list_for_each(&peer->channels, channel, list) { + if (channel_active(channel)) + return channel; + } + return NULL; +} + +struct channel *peer2channel(const struct peer *peer) +{ + return list_top(&peer->channels, struct channel, list); +} + +struct peer *channel2peer(const struct channel *channel) +{ + return channel->peer; +} diff --git a/lightningd/channel.h b/lightningd/channel.h new file mode 100644 index 000000000..d774c2844 --- /dev/null +++ b/lightningd/channel.h @@ -0,0 +1,193 @@ +#ifndef LIGHTNING_LIGHTNINGD_CHANNEL_H +#define LIGHTNING_LIGHTNINGD_CHANNEL_H +#include "config.h" +#include +#include +#include + +struct channel { + /* Inside peer->channels. */ + struct list_node list; + + /* Peer context */ + struct peer *peer; + + /* Database ID: 0 == not in db yet */ + u64 dbid; + + /* Error message (iff in error state) */ + u8 *error; + + /* Their shachain. */ + struct wallet_shachain their_shachain; + + /* What's happening. */ + enum peer_state state; + + /* Which side offered channel? */ + enum side funder; + + /* Command which ordered us to open channel, if any. */ + struct command *opening_cmd; + + /* Is there a single subdaemon responsible for us? */ + struct subd *owner; + + /* History */ + struct log *log; + + /* Channel flags from opening message. */ + u8 channel_flags; + + /* Our channel config. */ + struct channel_config our_config; + + /* Minimum funding depth (specified by us if they fund). */ + u32 minimum_depth; + + /* Tracking commitment transaction numbers. */ + u64 next_index[NUM_SIDES]; + u64 next_htlc_id; + + /* Funding txid and amounts (once known) */ + struct bitcoin_txid *funding_txid; + u16 funding_outnum; + u64 funding_satoshi, push_msat; + bool remote_funding_locked; + /* Channel if locked locally. */ + struct short_channel_id *scid; + + /* Amount going to us, not counting unfinished HTLCs; if we have one. */ + u64 *our_msatoshi; + + /* Last tx they gave us (if any). */ + struct bitcoin_tx *last_tx; + secp256k1_ecdsa_signature *last_sig; + secp256k1_ecdsa_signature *last_htlc_sigs; + + /* Keys for channel. */ + struct channel_info *channel_info; + + /* Secret seed (FIXME: Move to hsm!) */ + struct privkey seed; + + /* Their scriptpubkey if they sent shutdown. */ + u8 *remote_shutdown_scriptpubkey; + /* Our key for shutdown (-1 if not chosen yet) */ + s64 local_shutdown_idx; + + /* Reestablishment stuff: last sent commit and revocation details. */ + bool last_was_revoke; + struct changed_htlc *last_sent_commit; + + /* Blockheight at creation, scans for funding confirmations + * will start here */ + u64 first_blocknum; +}; + +struct channel *new_channel(struct peer *peer, u64 dbid, u32 first_blocknum); + +const char *channel_state_name(const struct channel *channel); + +void derive_channel_seed(struct lightningd *ld, struct privkey *seed, + const struct pubkey *peer_id, + const u64 dbid); + +/* FIXME: Temporary mapping from peer to channel, while we only have one. */ +struct channel *peer2channel(const struct peer *peer); +struct peer *channel2peer(const struct channel *channel); + +/* Find a channel which is not onchain, if any */ +struct channel *peer_active_channel(struct peer *peer); + +static inline bool channel_can_add_htlc(const struct channel *channel) +{ + return channel->state == CHANNELD_NORMAL; +} + +static inline bool channel_fees_can_change(const struct channel *channel) +{ + return channel->state == CHANNELD_NORMAL + || channel->state == CHANNELD_SHUTTING_DOWN; +} + +static inline bool channel_can_remove_htlc(const struct channel *channel) +{ + return channel->state == CHANNELD_NORMAL + || channel->state == CHANNELD_SHUTTING_DOWN + || channel->state == ONCHAIND_THEIR_UNILATERAL + || channel->state == ONCHAIND_OUR_UNILATERAL; +} + +static inline bool channel_state_on_chain(enum peer_state state) +{ + return state == ONCHAIND_CHEATED + || state == ONCHAIND_THEIR_UNILATERAL + || state == ONCHAIND_OUR_UNILATERAL + || state == ONCHAIND_MUTUAL; +} + +static inline bool channel_on_chain(const struct channel *channel) +{ + return channel_state_on_chain(channel->state); +} + +static inline bool channel_active(const struct channel *channel) +{ + return channel->state != FUNDING_SPEND_SEEN + && channel->state != CLOSINGD_COMPLETE + && !channel_on_chain(channel); +} + +static inline bool channel_wants_reconnect(const struct channel *channel) +{ + return channel->state >= CHANNELD_AWAITING_LOCKIN + && channel->state <= CLOSINGD_COMPLETE; +} + +/* BOLT #2: + * + * On disconnection, the funder MUST remember the channel for + * reconnection if it has broadcast the funding transaction, otherwise it + * SHOULD NOT. + * + * On disconnection, the non-funding node MUST remember the channel for + * reconnection if it has sent the `funding_signed` message, otherwise + * it SHOULD NOT. + */ +static inline bool channel_persists(const struct channel *channel) +{ + return channel->state >= CHANNELD_AWAITING_LOCKIN; +} + +/* FIXME: Obsolete */ +static inline bool peer_can_add_htlc(const struct peer *peer) +{ + return channel_can_add_htlc(peer2channel(peer)); +} + +static inline bool peer_fees_can_change(const struct peer *peer) +{ + return channel_fees_can_change(peer2channel(peer)); +} + +static inline bool peer_can_remove_htlc(const struct peer *peer) +{ + return channel_can_remove_htlc(peer2channel(peer)); +} + +static inline bool peer_on_chain(const struct peer *peer) +{ + return channel_state_on_chain(peer2channel(peer)->state); +} + +static inline bool peer_wants_reconnect(const struct peer *peer) +{ + return channel_wants_reconnect(peer2channel(peer)); +} + +static inline bool peer_persists(const struct peer *peer) +{ + return channel_persists(peer2channel(peer)); +} +#endif /* LIGHTNING_LIGHTNINGD_CHANNEL_H */