diff --git a/daemon/jsonrpc.c b/daemon/jsonrpc.c index c5798c5e6..f2e93b565 100644 --- a/daemon/jsonrpc.c +++ b/daemon/jsonrpc.c @@ -238,6 +238,7 @@ static const struct json_command *cmdlist[] = { &getlog_command, &connect_command, &getpeers_command, + &newhtlc_command, /* Developer/debugging options. */ &echo_command, &rhash_command, diff --git a/daemon/jsonrpc.h b/daemon/jsonrpc.h index b264e3bff..d1fbdab84 100644 --- a/daemon/jsonrpc.h +++ b/daemon/jsonrpc.h @@ -58,5 +58,5 @@ void setup_jsonrpc(struct lightningd_state *dstate, const char *rpc_filename); /* Commands (from other files) */ extern const struct json_command connect_command; extern const struct json_command getpeers_command; - +extern const struct json_command newhtlc_command; #endif /* LIGHTNING_DAEMON_JSONRPC_H */ diff --git a/daemon/packets.c b/daemon/packets.c index c6075b92f..537a71a60 100644 --- a/daemon/packets.c +++ b/daemon/packets.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #define FIXME_STUB(peer) do { log_broken((peer)->dstate->base_log, "%s:%u: Implement %s!", __FILE__, __LINE__, __func__); abort(); } while(0) @@ -99,7 +101,7 @@ Pkt *pkt_anchor(const tal_t *ctx, const struct peer *peer) a->amount = peer->anchor.satoshis; /* Sign their commit sig */ - peer_sign_theircommit(peer, &sig); + peer_sign_theircommit(peer, peer->them.commit, &sig); a->commit_sig = signature_to_proto(a, &sig); return make_pkt(ctx, PKT__PKT_OPEN_ANCHOR, a); @@ -115,7 +117,7 @@ Pkt *pkt_open_commit_sig(const tal_t *ctx, const struct peer *peer) dump_tx("Creating sig for:", peer->them.commit); dump_key("Using key:", &peer->us.commitkey); - peer_sign_theircommit(peer, &sig); + peer_sign_theircommit(peer, peer->them.commit, &sig); s->sig = signature_to_proto(s, &sig); return make_pkt(ctx, PKT__PKT_OPEN_COMMIT_SIG, s); @@ -132,7 +134,16 @@ Pkt *pkt_open_complete(const tal_t *ctx, const struct peer *peer) Pkt *pkt_htlc_update(const tal_t *ctx, const struct peer *peer, const struct htlc_progress *htlc_prog) { - FIXME_STUB(peer); + UpdateAddHtlc *u = tal(ctx, UpdateAddHtlc); + + update_add_htlc__init(u); + + u->revocation_hash = sha256_to_proto(u, &htlc_prog->our_revocation_hash); + u->amount_msat = htlc_prog->msatoshis; + u->r_hash = sha256_to_proto(u, &htlc_prog->rhash); + u->expiry = abs_locktime_to_proto(u, &htlc_prog->expiry); + + return make_pkt(ctx, PKT__PKT_UPDATE_ADD_HTLC, u); } Pkt *pkt_htlc_fulfill(const tal_t *ctx, const struct peer *peer, @@ -155,17 +166,50 @@ Pkt *pkt_htlc_routefail(const tal_t *ctx, const struct peer *peer, Pkt *pkt_update_accept(const tal_t *ctx, const struct peer *peer) { - FIXME_STUB(peer); + UpdateAccept *u = tal(ctx, UpdateAccept); + const struct htlc_progress *cur = peer->current_htlc; + struct signature sig; + + update_accept__init(u); + + dump_tx("Signing tx", cur->their_commit); + peer_sign_theircommit(peer, cur->their_commit, &sig); + u->sig = signature_to_proto(u, &sig); + u->revocation_hash + = sha256_to_proto(u, &cur->our_revocation_hash); + + return make_pkt(ctx, PKT__PKT_UPDATE_ACCEPT, u); } Pkt *pkt_update_signature(const tal_t *ctx, const struct peer *peer) { - FIXME_STUB(peer); + UpdateSignature *u = tal(ctx, UpdateSignature); + const struct htlc_progress *cur = peer->current_htlc; + struct signature sig; + struct sha256 preimage; + + update_signature__init(u); + + peer_sign_theircommit(peer, cur->their_commit, &sig); + u->sig = signature_to_proto(u, &sig); + assert(peer->num_htlcs > 0); + peer_get_revocation_preimage(peer, peer->num_htlcs-1, &preimage); + u->revocation_preimage = sha256_to_proto(u, &preimage); + + return make_pkt(ctx, PKT__PKT_UPDATE_SIGNATURE, u); } Pkt *pkt_update_complete(const tal_t *ctx, const struct peer *peer) { - FIXME_STUB(peer); + UpdateComplete *u = tal(ctx, UpdateComplete); + struct sha256 preimage; + + update_complete__init(u); + + peer_get_revocation_preimage(peer, peer->num_htlcs-1, &preimage); + u->revocation_preimage = sha256_to_proto(u, &preimage); + + return make_pkt(ctx, PKT__PKT_UPDATE_COMPLETE, u); } Pkt *pkt_err(const tal_t *ctx, const char *msg, ...) @@ -317,8 +361,49 @@ Pkt *accept_pkt_htlc_update(const tal_t *ctx, struct peer *peer, const Pkt *pkt, Pkt **decline) { - FIXME_STUB(peer); -} + const UpdateAddHtlc *u = pkt->update_add_htlc; + struct htlc_progress *cur = tal(peer, struct htlc_progress); + Pkt *err; + + cur->msatoshis = u->amount_msat; + proto_to_sha256(u->r_hash, &cur->rhash); + proto_to_sha256(u->revocation_hash, &cur->their_revocation_hash); + if (!proto_to_abs_locktime(u->expiry, &cur->expiry)) { + err = pkt_err(ctx, "Invalid HTLC expiry"); + goto fail; + } + cur->cstate = copy_funding(cur, peer->cstate); + if (!funding_delta(peer->them.offer_anchor == CMD_OPEN_WITH_ANCHOR, + peer->anchor.satoshis, + 0, cur->msatoshis, + &cur->cstate->b, &cur->cstate->a)) { + err = pkt_err(ctx, "Cannot afford %"PRIu64" milli-satoshis", + cur->msatoshis); + goto fail; + } + /* Add the htlc to their side of channel. */ + funding_add_htlc(&cur->cstate->b, cur->msatoshis, + &cur->expiry, &cur->rhash); + + peer_get_revocation_hash(peer, peer->num_htlcs+1, + &cur->our_revocation_hash); + + /* Now we create the commit tx pair. */ + make_commit_txs(cur, peer, &cur->our_revocation_hash, + &cur->their_revocation_hash, + cur->cstate, + &cur->our_commit, &cur->their_commit); + + /* FIXME: Fees must be sufficient. */ + *decline = NULL; + assert(!peer->current_htlc); + peer->current_htlc = cur; + return NULL; + +fail: + tal_free(cur); + return err; +}; Pkt *accept_pkt_htlc_routefail(const tal_t *ctx, struct peer *peer, const Pkt *pkt) @@ -338,23 +423,124 @@ Pkt *accept_pkt_htlc_fulfill(const tal_t *ctx, FIXME_STUB(peer); } +static u64 total_funds(const struct channel_oneside *c) +{ + u64 total = (u64)c->pay_msat + c->fee_msat; + size_t i, n = tal_count(c->htlcs); + + for (i = 0; i < n; i++) + total += c->htlcs[i].msatoshis; + return total; +} + +static void update_to_new_htlcs(struct peer *peer) +{ + struct htlc_progress *cur = peer->current_htlc; + + /* FIXME: Add to shachain too. */ + + /* HTLCs can't change total balance in channel! */ + if (total_funds(&peer->cstate->a) + total_funds(&peer->cstate->b) + != total_funds(&cur->cstate->a) + total_funds(&cur->cstate->b)) + fatal("Illegal funding transition from %u/%u (total %"PRIu64")" + " to %u/%u (total %"PRIu64")", + peer->cstate->a.pay_msat, peer->cstate->a.fee_msat, + total_funds(&peer->cstate->a), + peer->cstate->b.pay_msat, peer->cstate->b.fee_msat, + total_funds(&peer->cstate->b)); + + /* Now, we consider this channel_state current one. */ + tal_free(peer->cstate); + peer->cstate = tal_steal(peer, cur->cstate); + + tal_free(peer->us.commit); + peer->us.commit = tal_steal(peer, cur->our_commit); + /* FIXME: Save their old commit details, to steal funds. */ + tal_free(peer->them.commit); + peer->them.commit = tal_steal(peer, cur->their_commit); + peer->us.revocation_hash = cur->our_revocation_hash; + peer->them.revocation_hash = cur->their_revocation_hash; + + peer->num_htlcs++; +} + Pkt *accept_pkt_update_accept(const tal_t *ctx, struct peer *peer, const Pkt *pkt) { - FIXME_STUB(peer); + const UpdateAccept *a = pkt->update_accept; + struct htlc_progress *cur = peer->current_htlc; + + proto_to_sha256(a->revocation_hash, &cur->their_revocation_hash); + + cur->their_sig.stype = SIGHASH_ALL; + if (!proto_to_signature(a->sig, &cur->their_sig.sig)) + return pkt_err(ctx, "Malformed signature"); + + /* Now we can make commit tx pair. */ + make_commit_txs(cur, peer, &cur->our_revocation_hash, + &cur->their_revocation_hash, + cur->cstate, + &cur->our_commit, &cur->their_commit); + + /* Their sig should sign our new commit tx. */ + if (!check_tx_sig(peer->dstate->secpctx, + cur->our_commit, 0, + peer->anchor.redeemscript, + tal_count(peer->anchor.redeemscript), + &peer->them.commitkey, + &cur->their_sig)) + return pkt_err(ctx, "Bad signature"); + + /* Our next step will be to send the revocation preimage, so + * update to new HTLC now so we never use the old one. */ + update_to_new_htlcs(peer); + return NULL; +} + +static bool check_preimage(const Sha256Hash *preimage, const struct sha256 *hash) +{ + struct sha256 h; + + proto_to_sha256(preimage, &h); + sha256(&h, &h, sizeof(h)); + return structeq(&h, hash); } Pkt *accept_pkt_update_complete(const tal_t *ctx, struct peer *peer, const Pkt *pkt) { - FIXME_STUB(peer); + /* FIXME: Check preimage against old tx! */ + return NULL; } Pkt *accept_pkt_update_signature(const tal_t *ctx, struct peer *peer, const Pkt *pkt) { - FIXME_STUB(peer); + const UpdateSignature *s = pkt->update_signature; + struct htlc_progress *cur = peer->current_htlc; + + cur->their_sig.stype = SIGHASH_ALL; + if (!proto_to_signature(s->sig, &cur->their_sig.sig)) + return pkt_err(ctx, "Malformed signature"); + + /* Their sig should sign our new commit tx. */ + if (!check_tx_sig(peer->dstate->secpctx, + cur->our_commit, 0, + peer->anchor.redeemscript, + tal_count(peer->anchor.redeemscript), + &peer->them.commitkey, + &cur->their_sig)) + return pkt_err(ctx, "Bad signature"); + + /* Check their revocation preimage. */ + if (!check_preimage(s->revocation_preimage, &peer->them.revocation_hash)) + return pkt_err(ctx, "Bad revocation preimage"); + + /* Our next step will be to send the revocation preimage, so + * update to new HTLC now so we never use the old one. */ + update_to_new_htlcs(peer); + return NULL; } Pkt *accept_pkt_close(const tal_t *ctx, struct peer *peer, const Pkt *pkt) diff --git a/daemon/peer.c b/daemon/peer.c index 98c45a2cb..b0b5c66e3 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -17,10 +17,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -35,6 +37,17 @@ struct json_connecting { u64 satoshis; }; +static struct peer *find_peer(struct lightningd_state *dstate, + const struct pubkey *id) +{ + struct peer *peer; + list_for_each(&dstate->peers, peer, list) { + if (peer->state != STATE_INIT && pubkey_eq(&peer->id, id)) + return peer; + } + return NULL; +} + static void queue_output_pkt(struct peer *peer, Pkt *pkt) { peer->outpkt[peer->num_outpkt++] = pkt; @@ -236,6 +249,8 @@ static struct peer *new_peer(struct lightningd_state *dstate, list_head_init(&peer->watches); peer->num_outpkt = 0; peer->cmd = INPUT_NONE; + peer->current_htlc = NULL; + peer->num_htlcs = 0; /* If we free peer, conn should be closed, but can't be freed * immediately so don't make peer a parent. */ @@ -622,7 +637,7 @@ void peer_htlc_ours_deferred(struct peer *peer) /* Successfully added/fulfilled/timedout/routefail an HTLC. */ void peer_htlc_done(struct peer *peer) { - FIXME_STUB(peer); + peer->current_htlc = tal_free(peer->current_htlc); } /* Someone aborted an existing HTLC. */ @@ -898,3 +913,98 @@ const struct json_command getpeers_command = { "List the current peers", "Returns a 'peers' array" }; + +static void json_newhtlc(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + struct peer *peer; + jsmntok_t *idtok, *msatoshistok, *expirytok, *rhashtok; + struct pubkey id; + unsigned int expiry; + struct htlc_progress *cur; + + json_get_params(buffer, params, + "id", &idtok, + "msatoshis", &msatoshistok, + "expiry", &expirytok, + "rhash", &rhashtok, + NULL); + + if (!idtok || !msatoshistok || !expirytok || !rhashtok) { + command_fail(cmd, "Need id, msatoshis, expiry and rhash"); + return; + } + + if (!pubkey_from_hexstr(cmd->dstate->secpctx, + buffer + idtok->start, + idtok->end - idtok->start, &id)) { + command_fail(cmd, "Not a valid id"); + return; + } + peer = find_peer(cmd->dstate, &id); + if (!peer) { + command_fail(cmd, "Could not find peer with that id"); + return; + } + + /* Attach to cmd until it's complete. */ + cur = tal(cmd, struct htlc_progress); + if (!json_tok_u64(buffer, msatoshistok, &cur->msatoshis)) { + command_fail(cmd, "'%.*s' is not a valid number", + (int)(msatoshistok->end - msatoshistok->start), + buffer + msatoshistok->start); + return; + } + if (!json_tok_number(buffer, expirytok, &expiry)) { + command_fail(cmd, "'%.*s' is not a valid number", + (int)(expirytok->end - expirytok->start), + buffer + expirytok->start); + return; + } + + if (!seconds_to_abs_locktime(expiry, &cur->expiry)) { + command_fail(cmd, "'%.*s' is not a valid number", + (int)(expirytok->end - expirytok->start), + buffer + expirytok->start); + return; + } + if (!hex_decode(buffer + rhashtok->start, + rhashtok->end - rhashtok->start, + &cur->rhash, sizeof(cur->rhash))) { + command_fail(cmd, "'%.*s' is not a valid sha256 hash", + (int)(rhashtok->end - rhashtok->start), + buffer + rhashtok->start); + return; + } + peer_get_revocation_hash(peer, peer->num_htlcs+1, + &cur->our_revocation_hash); + + /* Can we even offer this much? */ + cur->cstate = copy_funding(cur, peer->cstate); + if (!funding_delta(peer->us.offer_anchor == CMD_OPEN_WITH_ANCHOR, + peer->anchor.satoshis, + 0, cur->msatoshis, + &cur->cstate->a, &cur->cstate->b)) { + command_fail(cmd, "Cannot afford %"PRIu64" milli-satoshis", + cur->msatoshis); + return; + } + /* Add the htlc to our side of channel. */ + funding_add_htlc(&cur->cstate->a, cur->msatoshis, + &cur->expiry, &cur->rhash); + + peer->current_htlc = tal_steal(peer, cur); + peer->jsoncmd = cmd; + + /* FIXME: do we need this? */ + peer->cmddata.htlc_prog = peer->current_htlc; + peer->cmd = CMD_SEND_HTLC_UPDATE; + try_command(peer); +} + +const struct json_command newhtlc_command = { + "newhtlc", + json_newhtlc, + "Offer {id} an HTLC worth {msatoshis} in {expiry} (in seconds since Jan 1 1970) with {rhash}", + "Returns an empty result on success" +}; diff --git a/daemon/peer.h b/daemon/peer.h index 55b88ce2c..78b982e59 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -29,6 +29,17 @@ struct peer_visible_state { struct bitcoin_tx *commit; }; +struct htlc_progress { + /* Channel funding state, after we've completed htlc. */ + struct channel_state *cstate; + struct abs_locktime expiry; + u64 msatoshis; + struct sha256 rhash; + struct sha256 our_revocation_hash, their_revocation_hash; + struct bitcoin_tx *our_commit, *their_commit; + struct bitcoin_signature their_sig; +}; + struct peer { /* dstate->peers list */ struct list_node list; @@ -79,6 +90,11 @@ struct peer { /* Their signature for our current commit sig. */ struct bitcoin_signature cur_commit_theirsig; + /* Current HTLC, if any. */ + struct htlc_progress *current_htlc; + /* Number of HTLC updates (== number of previous commit txs) */ + u64 num_htlcs; + /* Current ongoing packetflow */ struct io_data *io_data; diff --git a/daemon/secrets.c b/daemon/secrets.c index aa7e248d9..1bee2af24 100644 --- a/daemon/secrets.c +++ b/daemon/secrets.c @@ -41,11 +41,13 @@ struct peer_secrets { struct sha256 revocation_seed; }; -void peer_sign_theircommit(const struct peer *peer, struct signature *sig) +void peer_sign_theircommit(const struct peer *peer, + struct bitcoin_tx *commit, + struct signature *sig) { /* Commit tx only has one input: that of the anchor. */ sign_tx_input(peer->dstate->secpctx, - peer->them.commit, 0, + commit, 0, peer->anchor.redeemscript, tal_count(peer->anchor.redeemscript), &peer->secrets->commit, diff --git a/daemon/secrets.h b/daemon/secrets.h index b55365606..1125550ce 100644 --- a/daemon/secrets.h +++ b/daemon/secrets.h @@ -12,7 +12,9 @@ struct sha256; void privkey_sign(struct peer *peer, const void *src, size_t len, struct signature *sig); -void peer_sign_theircommit(const struct peer *peer, struct signature *sig); +void peer_sign_theircommit(const struct peer *peer, + struct bitcoin_tx *commit, + struct signature *sig); void peer_secrets_init(struct peer *peer); diff --git a/daemon/test/test.sh b/daemon/test/test.sh index dc5bc72a1..dc8349fee 100755 --- a/daemon/test/test.sh +++ b/daemon/test/test.sh @@ -53,6 +53,9 @@ while ! $LCLI2 getlog | grep 'listener on port'; do fi done +ID1=`$LCLI1 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'` +ID2=`$LCLI2 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'` + PORT2=`$LCLI2 getlog | sed -n 's/.*on port \([0-9]*\).*/\1/p'` $LCLI1 connect localhost $PORT2 999999 @@ -71,6 +74,11 @@ sleep 2 $LCLI1 getpeers | grep STATE_NORMAL_HIGHPRIO $LCLI2 getpeers | grep STATE_NORMAL_LOWPRIO +EXPIRY=$(( $(date +%s) + 1000)) +SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd +RHASH=`$LCLI1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'` +$LCLI1 newhtlc $ID2 1000000 $EXPIRY $RHASH + $LCLI1 stop $LCLI2 stop scripts/shutdown.sh