Browse Source

channel: unwrap and send incoming HTLCs to master.

So far it just looks it up, marks it resolved, then does nothing.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 8 years ago
parent
commit
be4af38d0c
  1. 4
      lightningd/Makefile
  2. 1
      lightningd/channel.c
  3. 2
      lightningd/channel/Makefile
  4. 93
      lightningd/channel/channel.c
  5. 1
      lightningd/channel/channel_wire.csv
  6. 103
      lightningd/peer_control.c
  7. 15
      lightningd/test/test-basic

4
lightningd/Makefile

@ -23,7 +23,6 @@ LIGHTNINGD_OLD_SRC := \
daemon/opt_time.c \
daemon/pseudorand.c \
daemon/routing.c \
daemon/sphinx.c \
daemon/watch.c
LIGHTNINGD_OLD_OBJS := $(LIGHTNINGD_OLD_SRC:.c=.o)
LIGHTNINGD_OLD_HEADERS := $(LIGHTNINGD_OLD_SRC:.c=.h)
@ -31,6 +30,7 @@ LIGHTNINGD_OLD_HEADERS := $(LIGHTNINGD_OLD_SRC:.c=.h)
LIGHTNINGD_OLD_LIB_SRC := \
daemon/htlc_state.c \
daemon/pseudorand.c \
daemon/sphinx.c \
daemon/timeout.c
LIGHTNINGD_OLD_LIB_OBJS := $(LIGHTNINGD_OLD_LIB_SRC:.c=.o)
LIGHTNINGD_OLD_LIB_HEADERS := $(LIGHTNINGD_OLD_LIB_SRC:.c=.h)
@ -104,11 +104,11 @@ 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
include lightningd/opening/Makefile
include lightningd/channel/Makefile
$(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS): $(LIGHTNINGD_HEADERS)

1
lightningd/channel.c

@ -455,7 +455,6 @@ enum channel_add_err channel_add_htlc(struct channel *channel,
assert(balance_msat >= 0);
for (i = 0; i < tal_count(adding); i++)
balance_msat += balance_adding_htlc(adding[i], sender);
assert(balance_msat >= 0);
/* This is a little subtle:
*

2
lightningd/channel/Makefile

@ -43,7 +43,7 @@ lightningd/channel/gen_channel_wire.c: $(WIRE_GEN) lightningd/channel/channel_wi
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) $(CCAN_SHACHAIN48_OBJ) $(LIGHTNINGD_HSM_CLIENT_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a libsodium.a libwallycore.a
lightningd/lightningd_channel: $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_CHANNEL_OBJS) $(WIRE_ONION_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(WIRE_OBJS) $(BITCOIN_OBJS) $(CCAN_OBJS) $(CCAN_SHACHAIN48_OBJ) $(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/%)

93
lightningd/channel/channel.c

@ -5,11 +5,13 @@
#include <ccan/crypto/shachain/shachain.h>
#include <ccan/fdpass/fdpass.h>
#include <ccan/io/io.h>
#include <ccan/mem/mem.h>
#include <ccan/structeq/structeq.h>
#include <ccan/take/take.h>
#include <ccan/tal/str/str.h>
#include <ccan/time/time.h>
#include <daemon/routing.h>
#include <daemon/sphinx.h>
#include <daemon/timeout.h>
#include <errno.h>
#include <inttypes.h>
@ -21,6 +23,7 @@
#include <lightningd/daemon_conn.h>
#include <lightningd/debug.h>
#include <lightningd/derive_basepoints.h>
#include <lightningd/hsm/gen_hsm_client_wire.h>
#include <lightningd/htlc_tx.h>
#include <lightningd/key_derive.h>
#include <lightningd/msg_queue.h>
@ -37,10 +40,11 @@
#include <wire/wire_io.h>
#include <wire/wire_sync.h>
/* stdin == requests, 3 == peer, 4 = gossip */
/* stdin == requests, 3 == peer, 4 = gossip, 5 = HSM */
#define REQ_FD STDIN_FILENO
#define PEER_FD 3
#define GOSSIP_FD 4
#define HSM_FD 5
struct peer {
struct peer_crypto_state pcs;
@ -514,7 +518,89 @@ static void our_htlc_failed(const struct htlc *htlc, struct peer *peer)
static void their_htlc_locked(const struct htlc *htlc, struct peer *peer)
{
status_trace("FIXME: their htlc %"PRIu64" locked", htlc->id);
tal_t *tmpctx = tal_tmpctx(peer);
u8 *msg;
struct onionpacket *op;
struct route_step *rs;
struct sha256 ss, bad_onion_sha;
enum onion_type failcode;
enum channel_remove_err rerr;
struct pubkey ephemeral;
status_trace("their htlc %"PRIu64" locked", htlc->id);
/* We unwrap the onion now. */
/* FIXME: We could do this earlier and call HSM async, for speed. */
op = parse_onionpacket(tmpctx, htlc->routing, TOTAL_PACKET_SIZE);
if (!op) {
/* FIXME: could be bad version, bad key. */
failcode = WIRE_INVALID_ONION_VERSION;
goto bad_onion;
}
/* Because wire takes struct pubkey. */
ephemeral.pubkey = op->ephemeralkey;
msg = towire_hsm_ecdh_req(tmpctx, &ephemeral);
if (!wire_sync_write(HSM_FD, msg))
status_failed(WIRE_CHANNEL_HSM_FAILED, "Writing ecdh req");
msg = wire_sync_read(tmpctx, HSM_FD);
if (!msg || !fromwire_hsm_ecdh_resp(msg, NULL, &ss))
status_failed(WIRE_CHANNEL_HSM_FAILED, "Reading ecdh response");
if (memeqzero(&ss, sizeof(ss))) {
failcode = WIRE_INVALID_ONION_KEY;
goto bad_onion;
}
rs = process_onionpacket(tmpctx, op, ss.u.u8, htlc->rhash.u.u8,
sizeof(htlc->rhash));
if (!rs) {
failcode = WIRE_INVALID_ONION_HMAC;
goto bad_onion;
}
/* Unknown realm isn't a bad onion, it's a normal failure. */
/* FIXME: Push complete hoppayload up and have master parse? */
if (rs->hoppayload->realm != 0) {
failcode = WIRE_INVALID_REALM;
msg = towire_update_fail_htlc(tmpctx, &peer->channel_id,
htlc->id, NULL);
msg_enqueue(&peer->peer_out, take(msg));
goto remove_htlc;
}
/* Tell master to deal with it. */
msg = towire_channel_accepted_htlc(tmpctx, htlc->id, htlc->msatoshi,
abs_locktime_to_blocks(&htlc->expiry),
&htlc->rhash,
serialize_onionpacket(tmpctx,
rs->next),
rs->nextcase == ONION_FORWARD,
rs->hoppayload->amt_to_forward,
rs->hoppayload->outgoing_cltv_value);
daemon_conn_send(&peer->master, take(msg));
tal_free(tmpctx);
return;
bad_onion:
sha256(&bad_onion_sha, htlc->routing, TOTAL_PACKET_SIZE);
msg = towire_update_fail_malformed_htlc(tmpctx, &peer->channel_id,
htlc->id, &bad_onion_sha,
failcode);
msg_enqueue(&peer->peer_out, take(msg));
remove_htlc:
status_trace("htlc %"PRIu64" %s", htlc->id, onion_type_name(failcode));
rerr = channel_fail_htlc(peer->channel, REMOTE, htlc->id);
if (rerr != CHANNEL_ERR_REMOVE_OK)
peer_failed(io_conn_fd(peer->peer_conn),
&peer->pcs.cs,
&peer->channel_id,
WIRE_CHANNEL_INTERNAL_ERROR,
"Could not fail malformed htlc %"PRIu64": %u",
htlc->id, rerr);
start_commit_timer(peer);
tal_free(tmpctx);
}
static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg)
@ -772,7 +858,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg)
u8 *msg;
u32 amount_msat, cltv_expiry;
struct sha256 payment_hash;
u8 onion_routing_packet[1254];
u8 onion_routing_packet[TOTAL_PACKET_SIZE];
enum onion_type failcode;
/* Subtle: must be tal_arr since we marshal using tal_len() */
const char *failmsg;
@ -933,6 +1019,7 @@ static struct io_plan *req_in(struct io_conn *conn, struct daemon_conn *master)
case WIRE_CHANNEL_BAD_COMMAND:
case WIRE_CHANNEL_HSM_FAILED:
case WIRE_CHANNEL_CRYPTO_FAILED:
case WIRE_CHANNEL_INTERNAL_ERROR:
case WIRE_CHANNEL_PEER_WRITE_FAILED:
case WIRE_CHANNEL_PEER_READ_FAILED:
case WIRE_CHANNEL_RECEIVED_FUNDING_LOCKED:

1
lightningd/channel/channel_wire.csv

@ -3,6 +3,7 @@ channel_bad_command,0x8000
# Also shouldn't happen
channel_hsm_failed,0x8001
channel_crypto_failed,0x8002
channel_internal_error,0x8003
# These are due to peer.
channel_peer_write_failed,0x8010

Can't render this file because it has a wrong number of fields in line 2.

103
lightningd/peer_control.c

@ -9,8 +9,10 @@
#include <ccan/tal/str/str.h>
#include <daemon/chaintopology.h>
#include <daemon/dns.h>
#include <daemon/invoice.h>
#include <daemon/jsonrpc.h>
#include <daemon/log.h>
#include <daemon/sphinx.h>
#include <errno.h>
#include <inttypes.h>
#include <lightningd/build_utxos.h>
@ -25,6 +27,7 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <wire/gen_onion_wire.h>
#include <wire/gen_peer_wire.h>
static void destroy_peer(struct peer *peer)
@ -616,6 +619,104 @@ static bool opening_got_hsm_funding_sig(struct subd *hsm, const u8 *resp,
return true;
}
struct decoding_htlc {
struct peer *peer;
u64 id;
u32 amount_msat;
u32 cltv_expiry;
struct sha256 payment_hash;
u8 onion[1254];
u8 shared_secret[32];
};
static void fail_htlc(struct peer *peer, u64 htlc_id, enum onion_type failcode)
{
log_broken(peer->log, "failed htlc %"PRIu64" code 0x%04x",
htlc_id, failcode);
/* FIXME: implement */
}
static void handle_localpay(struct peer *peer,
u64 htlc_id,
u32 amount_msat,
u32 cltv_expiry,
const struct sha256 *payment_hash)
{
struct invoice *invoice = find_unpaid(peer->ld->dstate.invoices,
payment_hash);
if (!invoice) {
fail_htlc(peer, htlc_id, WIRE_UNKNOWN_PAYMENT_HASH);
return;
}
/* BOLT #4:
*
* If the amount paid is less than the amount expected, the final node
* MUST fail the HTLC. If the amount paid is more than the amount
* expected, the final node SHOULD fail the HTLC:
*
* 1. type: PERM|16 (`incorrect_payment_amount`)
*/
if (amount_msat < invoice->msatoshi) {
fail_htlc(peer, htlc_id, WIRE_INCORRECT_PAYMENT_AMOUNT);
return;
} else if (amount_msat > invoice->msatoshi * 2) {
fail_htlc(peer, htlc_id, WIRE_INCORRECT_PAYMENT_AMOUNT);
return;
}
/* BOLT #4:
*
* 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);
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);
/* FIXME: msg = towire_channel_fulfill_htlc(htlc->id, &invoice->r); */
resolve_invoice(&peer->ld->dstate, invoice);
}
static int peer_accepted_htlc(struct peer *peer, const u8 *msg)
{
u64 id;
u32 cltv_expiry, amount_msat;
struct sha256 payment_hash;
u8 next_onion[TOTAL_PACKET_SIZE];
bool forward;
u64 amt_to_forward;
u32 outgoing_cltv_value;
if (!fromwire_channel_accepted_htlc(msg, NULL, &id, &amount_msat,
&cltv_expiry, &payment_hash,
next_onion, &forward,
&amt_to_forward,
&outgoing_cltv_value)) {
log_broken(peer->log, "bad fromwire_channel_accepted_htlc %s",
tal_hex(peer, msg));
return -1;
}
if (forward)
log_broken(peer->log, "FIXME: Implement forwarding!");
else
handle_localpay(peer, id,
amount_msat, cltv_expiry, &payment_hash);
return 0;
}
static int channel_msg(struct subd *sd, const u8 *msg, const int *unused)
{
enum channel_wire_type t = fromwire_peektype(msg);
@ -628,6 +729,7 @@ static int channel_msg(struct subd *sd, const u8 *msg, const int *unused)
peer_set_condition(sd->peer, "Normal operation");
break;
case WIRE_CHANNEL_ACCEPTED_HTLC:
return peer_accepted_htlc(sd->peer, msg);
case WIRE_CHANNEL_FULFILLED_HTLC:
case WIRE_CHANNEL_FAILED_HTLC:
case WIRE_CHANNEL_MALFORMED_HTLC:
@ -639,6 +741,7 @@ static int channel_msg(struct subd *sd, const u8 *msg, const int *unused)
case WIRE_CHANNEL_BAD_COMMAND:
case WIRE_CHANNEL_HSM_FAILED:
case WIRE_CHANNEL_CRYPTO_FAILED:
case WIRE_CHANNEL_INTERNAL_ERROR:
case WIRE_CHANNEL_PEER_WRITE_FAILED:
case WIRE_CHANNEL_PEER_READ_FAILED:
case WIRE_CHANNEL_PEER_BAD_MESSAGE:

15
lightningd/test/test-basic

@ -38,7 +38,7 @@ FUND_INPUT_TX=`$CLI getrawtransaction $FUND_INPUT_TXID`
lcli1 addfunds $FUND_INPUT_TX | $FGREP '"satoshis" : 10000002'
# Now fund a channel.
lcli1 fundchannel $ID2 100000
lcli1 fundchannel $ID2 1000000
# Now wait for it to reach depth
lcli1 getpeers info | $FGREP "Waiting for our funding tx"
@ -53,9 +53,22 @@ check "lcli2 getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'condition : Normal ope
SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd
RHASH=`lcli1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'`
# This is actually dust
lcli1 dev-newhtlc $ID2 100000 $(( $(blockheight) + 10 )) $RHASH
check "lcli1 getlog debug | $FGREP 'Sending commit_sig with 0 htlc sigs'"
check "lcli2 getlog debug | $FGREP 'their htlc 0 locked'"
check "lcli2 getpeers info | $FGREP 'failed htlc 0 code 0x400f'"
# This one isn't dust.
RHASH=`lcli2 invoice 100000000 testpayment1 | get_field rhash`
[ `lcli2 listinvoice testpayment1 | get_field complete` = false ]
lcli1 dev-newhtlc $ID2 100000000 $(( $(blockheight) + 10 )) $RHASH
check "lcli1 getlog debug | $FGREP 'Sending commit_sig with 1 htlc sigs'"
check "lcli2 getlog debug | $FGREP 'Resolving invoice '\'testpayment1\'' with HTLC 1'"
[ `lcli2 listinvoice testpayment1 | get_field complete` = true ]
lcli1 stop
lcli2 stop

Loading…
Cancel
Save