From 1e827998520822888655e8665ab45fb8bdb7d61d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 22 Jan 2016 06:45:28 +1030 Subject: [PATCH] daemon: fulfillhtlc command Signed-off-by: Rusty Russell --- daemon/jsonrpc.c | 1 + daemon/jsonrpc.h | 1 + daemon/packets.c | 55 ++++++++++++++++++++++++++-- daemon/peer.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ daemon/peer.h | 8 ++++- daemon/test/test.sh | 12 +++++++ 6 files changed, 161 insertions(+), 3 deletions(-) diff --git a/daemon/jsonrpc.c b/daemon/jsonrpc.c index f2e93b565..51d489c82 100644 --- a/daemon/jsonrpc.c +++ b/daemon/jsonrpc.c @@ -239,6 +239,7 @@ static const struct json_command *cmdlist[] = { &connect_command, &getpeers_command, &newhtlc_command, + &fulfillhtlc_command, /* Developer/debugging options. */ &echo_command, &rhash_command, diff --git a/daemon/jsonrpc.h b/daemon/jsonrpc.h index d1fbdab84..461c20544 100644 --- a/daemon/jsonrpc.h +++ b/daemon/jsonrpc.h @@ -59,4 +59,5 @@ void setup_jsonrpc(struct lightningd_state *dstate, const char *rpc_filename); extern const struct json_command connect_command; extern const struct json_command getpeers_command; extern const struct json_command newhtlc_command; +extern const struct json_command fulfillhtlc_command; #endif /* LIGHTNING_DAEMON_JSONRPC_H */ diff --git a/daemon/packets.c b/daemon/packets.c index deb8dea43..4e624d897 100644 --- a/daemon/packets.c +++ b/daemon/packets.c @@ -149,7 +149,14 @@ Pkt *pkt_htlc_update(const tal_t *ctx, const struct peer *peer, Pkt *pkt_htlc_fulfill(const tal_t *ctx, const struct peer *peer, const struct htlc_progress *htlc_prog) { - FIXME_STUB(peer); + UpdateFulfillHtlc *f = tal(ctx, UpdateFulfillHtlc); + + update_fulfill_htlc__init(f); + + f->revocation_hash = sha256_to_proto(f, &htlc_prog->our_revocation_hash); + f->r = sha256_to_proto(f, &htlc_prog->r); + + return make_pkt(ctx, PKT__PKT_UPDATE_FULFILL_HTLC, f); } Pkt *pkt_htlc_timedout(const tal_t *ctx, const struct peer *peer, @@ -437,7 +444,51 @@ Pkt *accept_pkt_htlc_timedout(const tal_t *ctx, Pkt *accept_pkt_htlc_fulfill(const tal_t *ctx, struct peer *peer, const Pkt *pkt) { - FIXME_STUB(peer); + const UpdateFulfillHtlc *f = pkt->update_fulfill_htlc; + struct htlc_progress *cur = tal(peer, struct htlc_progress); + Pkt *err; + size_t i; + struct sha256 rhash, r; + + proto_to_sha256(f->r, &r); + proto_to_sha256(f->revocation_hash, &cur->their_revocation_hash); + sha256(&rhash, &r, sizeof(r)); + i = funding_find_htlc(&peer->cstate->a, &rhash); + if (i == tal_count(peer->cstate->a.htlcs)) { + err = pkt_err(ctx, "Unknown HTLC"); + goto fail; + } + + cur->htlc = &peer->cstate->a.htlcs[i]; + + /* Removing it should not fail: they gain HTLC amount */ + cur->cstate = copy_funding(cur, peer->cstate); + if (!funding_delta(peer->us.offer_anchor == CMD_OPEN_WITH_ANCHOR, + peer->anchor.satoshis, + -cur->htlc->msatoshis, + -cur->htlc->msatoshis, + &cur->cstate->a, &cur->cstate->b)) { + fatal("Unexpected failure fulfilling HTLC of %"PRIu64 + " milli-satoshis", cur->htlc->msatoshis); + } + funding_remove_htlc(&cur->cstate->a, i); + + 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); + + assert(!peer->current_htlc); + peer->current_htlc = cur; + return NULL; + +fail: + tal_free(cur); + return err; } static u64 total_funds(const struct channel_oneside *c) diff --git a/daemon/peer.c b/daemon/peer.c index 2fbc3d055..9b4718966 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -1070,3 +1070,90 @@ const struct json_command newhtlc_command = { "Offer {id} an HTLC worth {msatoshis} in {expiry} (in seconds since Jan 1 1970) with {rhash}", "Returns an empty result on success" }; + +static void json_fulfillhtlc(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + struct peer *peer; + jsmntok_t *idtok, *rtok; + struct pubkey id; + struct sha256 rhash; + struct htlc_progress *cur; + size_t i; + + json_get_params(buffer, params, + "id", &idtok, + "r", &rtok, + NULL); + + if (!idtok || !rtok) { + command_fail(cmd, "Need id and r"); + 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 (!hex_decode(buffer + rtok->start, + rtok->end - rtok->start, + &cur->r, sizeof(cur->r))) { + command_fail(cmd, "'%.*s' is not a valid sha256 preimage", + (int)(rtok->end - rtok->start), + buffer + rtok->start); + return; + } + + sha256(&rhash, &cur->r, sizeof(cur->r)); + + i = funding_find_htlc(&peer->cstate->b, &rhash); + if (i == tal_count(peer->cstate->b.htlcs)) { + command_fail(cmd, "'%.*s' preimage htlc not found", + (int)(rtok->end - rtok->start), + buffer + rtok->start); + return; + } + cur->htlc = &peer->cstate->b.htlcs[i]; + + /* Removing it should not fail: we gain HTLC amount */ + cur->cstate = copy_funding(cur, peer->cstate); + if (!funding_delta(peer->them.offer_anchor == CMD_OPEN_WITH_ANCHOR, + peer->anchor.satoshis, + -cur->htlc->msatoshis, + -cur->htlc->msatoshis, + &cur->cstate->b, &cur->cstate->a)) { + fatal("Unexpected failure fulfilling HTLC of %"PRIu64 + " milli-satoshis", cur->htlc->msatoshis); + return; + } + funding_remove_htlc(&cur->cstate->b, i); + + peer_get_revocation_hash(peer, peer->num_htlcs+1, + &cur->our_revocation_hash); + + 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_FULFILL; + try_command(peer); +} + +const struct json_command fulfillhtlc_command = { + "fulfillhtlc", + json_fulfillhtlc, + "Redeem htlc proposed by {id} using {r}", + "Returns an empty result on success" +}; diff --git a/daemon/peer.h b/daemon/peer.h index 6baebe8ce..6b35b8964 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -30,9 +30,15 @@ struct peer_visible_state { }; struct htlc_progress { + /* The HTLC we're working on. */ + struct channel_htlc *htlc; + + /* Set if we're fulfilling. */ + struct sha256 r; + + /* Our next state. */ /* Channel funding state, after we've completed htlc. */ struct channel_state *cstate; - struct channel_htlc *htlc; struct sha256 our_revocation_hash, their_revocation_hash; struct bitcoin_tx *our_commit, *their_commit; struct bitcoin_signature their_sig; diff --git a/daemon/test/test.sh b/daemon/test/test.sh index 9391b62d4..5a7363bf3 100755 --- a/daemon/test/test.sh +++ b/daemon/test/test.sh @@ -93,6 +93,18 @@ $LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"them" : { "pay" : 0, "fee" $LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"them" : { "pay" : 948999000, "fee" : 50000000, "htlcs" : [ { "msatoshis" : 1000000, "expiry" : { "second" : '$EXPIRY' }, "rhash" : "'$RHASH'" } ]' $LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"us" : { "pay" : 0, "fee" : 0, "htlcs" : [ ]' +$LCLI2 fulfillhtlc $ID1 $SECRET + +$LCLI1 getpeers +# We've transferred the HTLC amount to 2, who now has to pay fees. +$LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"us" : { "pay" : 949999000, "fee" : 49000000, "htlcs" : [ ]' +$LCLI1 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"them" : { "pay" : 0, "fee" : 1000000, "htlcs" : [ ]' + +$LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"them" : { "pay" : 949999000, "fee" : 49000000, "htlcs" : [ ]' +$LCLI2 getpeers | tr -s '\012\011 ' ' ' | fgrep -q '"us" : { "pay" : 0, "fee" : 1000000, "htlcs" : [ ]' + +sleep 1 + $LCLI1 stop $LCLI2 stop scripts/shutdown.sh