mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
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>
This commit is contained in:
parent
8a84e961ed
commit
be4af38d0c
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
*
|
||||
|
@ -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/%)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
|
@ -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:
|
||||
|
@ -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…
Reference in New Issue
Block a user