mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
daemon: fulfillhtlc command
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
17359279b2
commit
1e82799852
@ -239,6 +239,7 @@ static const struct json_command *cmdlist[] = {
|
|||||||
&connect_command,
|
&connect_command,
|
||||||
&getpeers_command,
|
&getpeers_command,
|
||||||
&newhtlc_command,
|
&newhtlc_command,
|
||||||
|
&fulfillhtlc_command,
|
||||||
/* Developer/debugging options. */
|
/* Developer/debugging options. */
|
||||||
&echo_command,
|
&echo_command,
|
||||||
&rhash_command,
|
&rhash_command,
|
||||||
|
@ -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 connect_command;
|
||||||
extern const struct json_command getpeers_command;
|
extern const struct json_command getpeers_command;
|
||||||
extern const struct json_command newhtlc_command;
|
extern const struct json_command newhtlc_command;
|
||||||
|
extern const struct json_command fulfillhtlc_command;
|
||||||
#endif /* LIGHTNING_DAEMON_JSONRPC_H */
|
#endif /* LIGHTNING_DAEMON_JSONRPC_H */
|
||||||
|
@ -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,
|
Pkt *pkt_htlc_fulfill(const tal_t *ctx, const struct peer *peer,
|
||||||
const struct htlc_progress *htlc_prog)
|
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,
|
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,
|
Pkt *accept_pkt_htlc_fulfill(const tal_t *ctx,
|
||||||
struct peer *peer, const Pkt *pkt)
|
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)
|
static u64 total_funds(const struct channel_oneside *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}",
|
"Offer {id} an HTLC worth {msatoshis} in {expiry} (in seconds since Jan 1 1970) with {rhash}",
|
||||||
"Returns an empty result on success"
|
"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"
|
||||||
|
};
|
||||||
|
@ -30,9 +30,15 @@ struct peer_visible_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct htlc_progress {
|
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. */
|
/* Channel funding state, after we've completed htlc. */
|
||||||
struct channel_state *cstate;
|
struct channel_state *cstate;
|
||||||
struct channel_htlc *htlc;
|
|
||||||
struct sha256 our_revocation_hash, their_revocation_hash;
|
struct sha256 our_revocation_hash, their_revocation_hash;
|
||||||
struct bitcoin_tx *our_commit, *their_commit;
|
struct bitcoin_tx *our_commit, *their_commit;
|
||||||
struct bitcoin_signature their_sig;
|
struct bitcoin_signature their_sig;
|
||||||
|
@ -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 '"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 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
|
$LCLI1 stop
|
||||||
$LCLI2 stop
|
$LCLI2 stop
|
||||||
scripts/shutdown.sh
|
scripts/shutdown.sh
|
||||||
|
Loading…
Reference in New Issue
Block a user