From 9014f2593e21931d6de3bd0e1f4c0df0d3d53fbe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 7 Mar 2017 11:56:12 +1030 Subject: [PATCH] lightningd/channel: normal operation daemon. Signed-off-by: Rusty Russell --- lightningd/Makefile | 3 +- lightningd/channel/Makefile | 66 ++++++ lightningd/channel/channel.c | 226 ++++++++++++++++++++ lightningd/channel/channel_control_wire.csv | 24 +++ lightningd/channel/channel_status_wire.csv | 14 ++ lightningd/test/test-basic | 3 + 6 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 lightningd/channel/Makefile create mode 100644 lightningd/channel/channel.c create mode 100644 lightningd/channel/channel_control_wire.csv create mode 100644 lightningd/channel/channel_status_wire.csv diff --git a/lightningd/Makefile b/lightningd/Makefile index 772b93e7f..9df47fd45 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -4,7 +4,7 @@ lightningd-wrongdir: $(MAKE) -C .. lightningd-all -lightningd-all: lightningd/lightningd lightningd/lightningd_hsm lightningd/lightningd_handshake lightningd/lightningd_gossip lightningd/lightningd_opening +lightningd-all: lightningd/lightningd lightningd/lightningd_hsm lightningd/lightningd_handshake lightningd/lightningd_gossip lightningd/lightningd_opening lightningd/lightningd_channel default: lightningd-all @@ -98,6 +98,7 @@ LIGHTNINGD_HEADERS = $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) $(LIG # These included makefiles add their headers to the LIGHTNINGD_HEADERS # variable so the include must preceed any actual use of the variable. +include lightningd/channel/Makefile include lightningd/hsm/Makefile include lightningd/handshake/Makefile include lightningd/gossip/Makefile diff --git a/lightningd/channel/Makefile b/lightningd/channel/Makefile new file mode 100644 index 000000000..6b63bfae2 --- /dev/null +++ b/lightningd/channel/Makefile @@ -0,0 +1,66 @@ +#! /usr/bin/make + +# Designed to be run one level up +lightningd/channel-wrongdir: + $(MAKE) -C ../.. lightningd/channel-all + +default: lightningd/channel-all + +lightningd/channel-all: lightningd/lightningd_channel + +# lightningd/channel needs these: +LIGHTNINGD_CHANNEL_HEADERS_GEN := \ + lightningd/channel/gen_channel_control_wire.h \ + lightningd/channel/gen_channel_status_wire.h + +LIGHTNINGD_CHANNEL_HEADERS_NOGEN := + +LIGHTNINGD_CHANNEL_HEADERS := $(LIGHTNINGD_CHANNEL_HEADERS_GEN) $(LIGHTNINGD_CHANNEL_HEADERS_NOGEN) + +LIGHTNINGD_CHANNEL_SRC := lightningd/channel/channel.c \ + $(LIGHTNINGD_CHANNEL_HEADERS:.h=.c) +LIGHTNINGD_CHANNEL_OBJS := $(LIGHTNINGD_CHANNEL_SRC:.c=.o) + +# Control daemon uses this: +LIGHTNINGD_CHANNEL_CONTROL_HEADERS := $(LIGHTNINGD_CHANNEL_HEADERS) +LIGHTNINGD_CHANNEL_CONTROL_SRC := $(LIGHTNINGD_CHANNEL_HEADERS:.h=.c) +LIGHTNINGD_CHANNEL_CONTROL_OBJS := $(LIGHTNINGD_CHANNEL_CONTROL_SRC:.c=.o) + +LIGHTNINGD_CHANNEL_GEN_SRC := $(filter lightningd/channel/gen_%, $(LIGHTNINGD_CHANNEL_SRC) $(LIGHTNINGD_CHANNEL_CONTROL_SRC)) + +LIGHTNINGD_CHANNEL_SRC_NOGEN := $(filter-out lightningd/channel/gen_%, $(LIGHTNINGD_CHANNEL_SRC)) + +# Add to headers which any object might need. +LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_CHANNEL_HEADERS_GEN) +LIGHTNINGD_HEADERS_NOGEN += $(LIGHTNINGD_CHANNEL_HEADERS_NOGEN) + +$(LIGHTNINGD_CHANNEL_OBJS): $(LIGHTNINGD_HEADERS) + +lightningd/channel/gen_channel_control_wire.h: $(WIRE_GEN) lightningd/channel/channel_control_wire.csv + $(WIRE_GEN) --header $@ channel_control_wire_type < lightningd/channel/channel_control_wire.csv > $@ + +lightningd/channel/gen_channel_control_wire.c: $(WIRE_GEN) lightningd/channel/channel_control_wire.csv + $(WIRE_GEN) ${@:.c=.h} channel_control_wire_type < lightningd/channel/channel_control_wire.csv > $@ + +lightningd/channel/gen_channel_status_wire.h: $(WIRE_GEN) lightningd/channel/channel_status_wire.csv + $(WIRE_GEN) --header $@ channel_status_wire_type < lightningd/channel/channel_status_wire.csv > $@ + +lightningd/channel/gen_channel_status_wire.c: $(WIRE_GEN) lightningd/channel/channel_status_wire.csv + $(WIRE_GEN) ${@:.c=.h} channel_status_wire_type < lightningd/channel/channel_status_wire.csv > $@ + +LIGHTNINGD_CHANNEL_OBJS := $(LIGHTNINGD_CHANNEL_SRC:.c=.o) $(LIGHTNINGD_CHANNEL_GEN_SRC:.c=.o) + +lightningd/lightningd_channel: $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_CHANNEL_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(WIRE_OBJS) $(BITCOIN_OBJS) $(CCAN_OBJS) $(LIGHTNINGD_HSM_CLIENT_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a libsodium.a libwallycore.a + $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) + +check-source: $(LIGHTNINGD_CHANNEL_SRC_NOGEN:%=check-src-include-order/%) +check-source-bolt: $(LIGHTNINGD_CHANNEL_SRC:%=bolt-check/%) $(LIGHTNINGD_CHANNEL_HEADERS:%=bolt-check/%) + +check-whitespace: $(LIGHTNINGD_CHANNEL_SRC_NOGEN:%=check-whitespace/%) $(LIGHTNINGD_CHANNEL_HEADERS_NOGEN:%=check-whitespace/%) + +clean: lightningd/channel-clean + +lightningd/channel-clean: + $(RM) $(LIGHTNINGD_CHANNEL_OBJS) gen_* + +-include lightningd/channel/test/Makefile diff --git a/lightningd/channel/channel.c b/lightningd/channel/channel.c new file mode 100644 index 000000000..4d5c32127 --- /dev/null +++ b/lightningd/channel/channel.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Stdout == status, stdin == requests, 3 == peer */ +#define STATUS_FD STDOUT_FILENO +#define REQ_FD STDIN_FILENO +#define PEER_FD 3 + +struct peer { + struct peer_crypto_state pcs; + struct channel_config conf[NUM_SIDES]; + struct pubkey next_per_commit[NUM_SIDES]; + bool funding_locked[NUM_SIDES]; + + /* Their sig for current commit. */ + secp256k1_ecdsa_signature their_commit_sig; + + /* Secret keys and basepoint secrets. */ + struct secrets our_secrets; + + /* Our shaseed for generating per-commitment-secrets. */ + struct sha256 shaseed; + + struct channel_id channel_id; + struct channel *channel; + + u8 *req_in; + const u8 **peer_out; +}; + +static void msg_enqueue(const u8 ***q, const u8 *add) +{ + size_t n = tal_count(*q); + tal_resize(q, n+1); + (*q)[n] = add; +} + +static const u8 *msg_dequeue(const u8 ***q) +{ + size_t n = tal_count(*q); + const u8 *msg; + + if (!n) + return NULL; + msg = (*q)[0]; + memmove(*q, *q + 1, sizeof(**q) * (n-1)); + tal_resize(q, n-1); + return msg; +} + +static void queue_pkt(struct peer *peer, const u8 *msg) +{ + msg_enqueue(&peer->peer_out, msg); + io_wake(peer); +} + +static struct io_plan *peer_out(struct io_conn *conn, struct peer *peer) +{ + const u8 *out = msg_dequeue(&peer->peer_out); + if (!out) + return io_out_wait(conn, peer, peer_out, peer); + + return peer_write_message(conn, &peer->pcs, out, peer_out); +} + +static struct io_plan *peer_in(struct io_conn *conn, struct peer *peer, u8 *msg) +{ + struct channel_id chanid; + + status_trace("Received %s from peer", + wire_type_name(fromwire_peektype(msg))); + + if (fromwire_funding_locked(msg, NULL, &chanid, + &peer->next_per_commit[REMOTE])) { + if (!structeq(&chanid, &peer->channel_id)) + status_failed(WIRE_CHANNEL_PEER_BAD_MESSAGE, + "Wrong channel id in %s", + tal_hex(trc, msg)); + if (peer->funding_locked[REMOTE]) + status_failed(WIRE_CHANNEL_PEER_BAD_MESSAGE, + "Funding locked twice"); + peer->funding_locked[REMOTE] = true; + status_send(towire_channel_received_funding_locked(peer)); + + if (peer->funding_locked[LOCAL]) + status_send(towire_channel_normal_operation(peer)); + } + /* FIXME: Process gossip. */ + + return peer_read_message(conn, &peer->pcs, peer_in); +} + +static struct io_plan *req_in(struct io_conn *conn, struct peer *peer) +{ + if (fromwire_channel_funding_locked(peer->req_in, NULL)) { + u8 *msg = towire_funding_locked(peer, + &peer->channel_id, + &peer->next_per_commit[LOCAL]); + queue_pkt(peer, msg); + peer->funding_locked[LOCAL] = true; + + if (peer->funding_locked[REMOTE]) + status_send(towire_channel_normal_operation(peer)); + } else + status_failed(WIRE_CHANNEL_BAD_COMMAND, "%s", strerror(errno)); + + return io_read_wire(conn, peer, &peer->req_in, req_in, peer); +} + +static struct io_plan *setup_req_in(struct io_conn *conn, struct peer *peer) +{ + return io_read_wire(conn, peer, &peer->req_in, req_in, peer); +} + +static struct io_plan *setup_peer_conn(struct io_conn *conn, struct peer *peer) +{ + return io_duplex(conn, peer_read_message(conn, &peer->pcs, peer_in), + peer_out(conn, peer)); +} + +static void peer_conn_broken(struct io_conn *conn, struct peer *peer) +{ + status_failed(WIRE_CHANNEL_PEER_READ_FAILED, + "peer connection broken: %s", strerror(errno)); +} + +#ifndef TESTING +int main(int argc, char *argv[]) +{ + u8 *msg; + struct peer *peer = tal(NULL, struct peer); + struct privkey seed; + struct basepoints points[NUM_SIDES]; + struct pubkey funding_pubkey[NUM_SIDES]; + u32 feerate; + u64 funding_satoshi, push_msat; + u16 funding_txout; + struct sha256_double funding_txid; + bool am_funder; + + if (argc == 2 && streq(argv[1], "--version")) { + printf("%s\n", version()); + exit(0); + } + + subdaemon_debug(argc, argv); + + /* We handle write returning errors! */ + signal(SIGCHLD, SIG_IGN); + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY + | SECP256K1_CONTEXT_SIGN); + status_setup(STATUS_FD); + peer->peer_out = tal_arr(peer, const u8 *, 0); + init_peer_crypto_state(peer, &peer->pcs); + peer->funding_locked[LOCAL] = peer->funding_locked[REMOTE] = false; + + msg = wire_sync_read(peer, REQ_FD); + if (!msg) + status_failed(WIRE_CHANNEL_BAD_COMMAND, "%s", strerror(errno)); + + if (!fromwire_channel_init(msg, NULL, + &funding_txid, &funding_txout, + &peer->conf[LOCAL], &peer->conf[REMOTE], + &peer->their_commit_sig, + &peer->pcs.cs, + &funding_pubkey[REMOTE], + &points[REMOTE].revocation, + &points[REMOTE].payment, + &points[REMOTE].delayed_payment, + &peer->next_per_commit[REMOTE], + &am_funder, + &feerate, &funding_satoshi, &push_msat, + &seed)) + status_failed(WIRE_CHANNEL_BAD_COMMAND, "%s", + tal_hex(msg, msg)); + tal_free(msg); + + /* We derive everything from the one secret seed. */ + derive_basepoints(&seed, &funding_pubkey[LOCAL], &points[LOCAL], + &peer->our_secrets, &peer->shaseed, + &peer->next_per_commit[LOCAL], 1); + + peer->channel = new_channel(peer, &funding_txid, funding_txout, + funding_satoshi, push_msat, feerate, + &peer->conf[LOCAL], &peer->conf[REMOTE], + &points[LOCAL], &points[REMOTE], + am_funder ? LOCAL : REMOTE); + + /* OK, now we can process peer messages. */ + io_set_finish(io_new_conn(peer, PEER_FD, setup_peer_conn, peer), + peer_conn_broken, peer); + io_new_conn(peer, REQ_FD, setup_req_in, peer); + + /* We don't expect to exit here. */ + io_loop(NULL, NULL); + tal_free(peer); + return 0; +} +#endif /* TESTING */ diff --git a/lightningd/channel/channel_control_wire.csv b/lightningd/channel/channel_control_wire.csv new file mode 100644 index 000000000..ee738f88b --- /dev/null +++ b/lightningd/channel/channel_control_wire.csv @@ -0,0 +1,24 @@ +#include +#include + +# Begin! You're still waiting for the tx to be buried though. +channel_init,1 +channel_init,0,funding_txid,32,struct sha256_double +channel_init,32,funding_txout,2 +channel_init,34,our_config,36,struct channel_config +channel_init,70,their_config,36,struct channel_config +channel_init,106,first_commit_sig,64,secp256k1_ecdsa_signature +channel_init,166,crypto_state,144,struct crypto_state +channel_init,310,remote_fundingkey,33 +channel_init,343,revocation_basepoint,33 +channel_init,376,payment_basepoint,33 +channel_init,409,delayed_payment_basepoint,33 +channel_init,442,their_per_commit_point,33 +channel_init,475,am_funder,1,bool +channel_init,476,feerate,4 +channel_init,480,funding_satoshi,8 +channel_init,488,push_msat,8 +channel_init,496,seed,32,struct privkey + +# Tx is deep enough, go! +channel_funding_locked,2 diff --git a/lightningd/channel/channel_status_wire.csv b/lightningd/channel/channel_status_wire.csv new file mode 100644 index 000000000..8e40269a0 --- /dev/null +++ b/lightningd/channel/channel_status_wire.csv @@ -0,0 +1,14 @@ +# Shouldn't happen +channel_bad_command,0x8000 +# Also shouldn't happen +channel_hsm_failed,0x8001 + +# These are due to peer. +channel_peer_write_failed,0x8010 +channel_peer_read_failed,0x8011 +channel_peer_bad_message,0x8012 + +# Received funding_locked +channel_received_funding_locked,1 +# Received and sent fundin_locked +channel_normal_operation,2 diff --git a/lightningd/test/test-basic b/lightningd/test/test-basic index 071361327..ae21b1dd0 100755 --- a/lightningd/test/test-basic +++ b/lightningd/test/test-basic @@ -47,6 +47,9 @@ $CLI generate 10 check "lcli1 getpeers info | $FGREP 'Funding tx reached depth'" check "lcli2 getpeers info | $FGREP 'Funding tx reached depth'" +check "lcli1 getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'condition : Normal operation'" +check "lcli2 getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'condition : Normal operation'" + lcli1 stop lcli2 stop