Skip to content

Commit 2c15dc0

Browse files
committed
xpay: make self-pay work.
Signed-off-by: Rusty Russell <[email protected]>
1 parent 229fc3f commit 2c15dc0

File tree

2 files changed

+87
-28
lines changed

2 files changed

+87
-28
lines changed

plugins/xpay/xpay.c

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ struct attempt {
139139
struct amount_msat delivers;
140140

141141
/* Path we tried, so we can unreserve, and tell askrene the results */
142-
struct hop *hops;
142+
const struct hop *hops;
143143

144144
/* Secrets, so we can decrypt error onions */
145145
struct secret *shared_secrets;
@@ -334,16 +334,31 @@ static void payment_failed(struct command *aux_cmd,
334334
cleanup(aux_cmd, payment);
335335
}
336336

337+
/* For self-pay, we don't have hops. */
338+
static struct amount_msat initial_sent(const struct attempt *attempt)
339+
{
340+
if (tal_count(attempt->hops) == 0)
341+
return attempt->delivers;
342+
return attempt->hops[0].amount_in;
343+
}
344+
345+
static u32 initial_cltv_delta(const struct attempt *attempt)
346+
{
347+
if (tal_count(attempt->hops) == 0)
348+
return attempt->payment->final_cltv;
349+
return attempt->hops[0].cltv_value_in;
350+
}
351+
337352
/* The current attempt is the first to succeed: we assume all the ones
338353
* in progress will succeed too */
339354
static struct amount_msat total_sent(const struct payment *payment,
340355
const struct attempt *attempt)
341356
{
342-
struct amount_msat total = attempt->hops[0].amount_in;
357+
struct amount_msat total = initial_sent(attempt);
343358
const struct attempt *i;
344359

345360
list_for_each(&payment->current_attempts, i, list) {
346-
if (!amount_msat_accumulate(&total, attempt->hops[0].amount_in))
361+
if (!amount_msat_accumulate(&total, initial_sent(i)))
347362
abort();
348363
}
349364
return total;
@@ -855,18 +870,13 @@ static const u8 *create_onion(const tal_t *ctx, struct attempt *attempt)
855870
return ret;
856871
}
857872

858-
static struct command_result *reserve_done(struct command *aux_cmd,
859-
const char *method,
860-
const char *buf,
861-
const jsmntok_t *result,
862-
struct attempt *attempt)
873+
static struct command_result *do_inject(struct command *aux_cmd,
874+
struct attempt *attempt)
863875
{
864876
struct out_req *req;
865877
const u8 *onion;
866878
struct xpay *xpay = xpay_of(attempt->payment->plugin);
867879

868-
attempt_debug(attempt, "%s", "Reserve done!");
869-
870880
onion = create_onion(tmpctx, attempt);
871881
/* FIXME: Handle this better! */
872882
if (!onion) {
@@ -882,14 +892,26 @@ static struct command_result *reserve_done(struct command *aux_cmd,
882892
attempt);
883893
json_add_hex_talarr(req->js, "onion", onion);
884894
json_add_sha256(req->js, "payment_hash", &attempt->payment->payment_hash);
885-
json_add_amount_msat(req->js, "amount_msat", attempt->hops[0].amount_in);
886-
json_add_u32(req->js, "cltv_expiry", attempt->hops[0].cltv_value_in + xpay->blockheight);
895+
/* If no route, its the same as delivery (self-pay) */
896+
json_add_amount_msat(req->js, "amount_msat", initial_sent(attempt));
897+
json_add_u32(req->js, "cltv_expiry", initial_cltv_delta(attempt) + xpay->blockheight);
887898
json_add_u64(req->js, "partid", attempt->partid);
888899
json_add_u64(req->js, "groupid", attempt->payment->group_id);
889900
json_add_string(req->js, "invstring", attempt->payment->invstring);
890901
return send_payment_req(aux_cmd, attempt->payment, req);
891902
}
892903

904+
static struct command_result *reserve_done(struct command *aux_cmd,
905+
const char *method,
906+
const char *buf,
907+
const jsmntok_t *result,
908+
struct attempt *attempt)
909+
{
910+
attempt_debug(attempt, "%s", "Reserve done!");
911+
912+
return do_inject(aux_cmd, attempt);
913+
}
914+
893915
static struct command_result *reserve_done_err(struct command *aux_cmd,
894916
const char *method,
895917
const char *buf,
@@ -903,6 +925,22 @@ static struct command_result *reserve_done_err(struct command *aux_cmd,
903925
return command_still_pending(aux_cmd);
904926
}
905927

928+
/* Does not set shared_secrets */
929+
static struct attempt *new_attempt(struct payment *payment,
930+
struct amount_msat delivers,
931+
const struct hop *hops TAKES)
932+
{
933+
struct attempt *attempt = tal(payment, struct attempt);
934+
935+
attempt->payment = payment;
936+
attempt->delivers = delivers;
937+
attempt->partid = ++payment->total_num_attempts;
938+
attempt->hops = tal_dup_talarr(attempt, struct hop, hops);
939+
list_add_tail(&payment->current_attempts, &attempt->list);
940+
941+
return attempt;
942+
}
943+
906944
static struct command_result *getroutes_done(struct command *aux_cmd,
907945
const char *method,
908946
const char *buf,
@@ -955,16 +993,17 @@ static struct command_result *getroutes_done(struct command *aux_cmd,
955993
size_t j;
956994
const jsmntok_t *hoptok, *path;
957995
struct out_req *req;
958-
struct attempt *attempt = tal(payment, struct attempt);
996+
struct amount_msat delivers;
997+
struct hop *hops;
998+
struct attempt *attempt;
999+
9591000
json_to_msat(buf, json_get_member(buf, t, "amount_msat"),
960-
&attempt->delivers);
1001+
&delivers);
9611002
path = json_get_member(buf, t, "path");
962-
attempt->hops = tal_arr(attempt, struct hop, path->size);
963-
attempt->payment = payment;
964-
attempt->partid = ++payment->total_num_attempts;
1003+
hops = tal_arr(NULL, struct hop, path->size);
9651004
json_for_each_arr(j, hoptok, path) {
9661005
const char *err;
967-
struct hop *hop = &attempt->hops[j];
1006+
struct hop *hop = &hops[j];
9681007
err = json_scan(tmpctx, buf, hoptok,
9691008
"{short_channel_id_dir:%"
9701009
",amount_msat:%"
@@ -979,14 +1018,13 @@ static struct command_result *getroutes_done(struct command *aux_cmd,
9791018
plugin_err(aux_cmd->plugin, "Malformed routes: %s",
9801019
err);
9811020
if (j > 0) {
982-
attempt->hops[j-1].amount_out = hop->amount_in;
983-
attempt->hops[j-1].cltv_value_out = hop->cltv_value_in;
1021+
hops[j-1].amount_out = hop->amount_in;
1022+
hops[j-1].cltv_value_out = hop->cltv_value_in;
9841023
}
9851024
}
986-
attempt->hops[j-1].amount_out = attempt->delivers;
987-
attempt->hops[j-1].cltv_value_out = attempt->payment->final_cltv;
988-
989-
list_add_tail(&payment->current_attempts, &attempt->list);
1025+
hops[j-1].amount_out = delivers;
1026+
hops[j-1].cltv_value_out = payment->final_cltv;
1027+
attempt = new_attempt(payment, delivers, take(hops));
9901028

9911029
/* Reserve this route */
9921030
attempt_debug(attempt, "%s", "doing askrene-reserve");
@@ -1053,20 +1091,30 @@ static struct command_result *getroutes_for(struct command *aux_cmd,
10531091
{
10541092
struct xpay *xpay = xpay_of(aux_cmd->plugin);
10551093
struct out_req *req;
1094+
const struct pubkey *dst;
10561095

10571096
/* If we get injectpaymentonion responses, they can wait */
10581097
payment->amount_being_routed = deliver;
10591098

1099+
if (payment->paths)
1100+
dst = &xpay->fakenode;
1101+
else
1102+
dst = &payment->destination;
1103+
1104+
/* Self-pay? Shortcut all this */
1105+
if (pubkey_eq(&xpay->local_id, dst)) {
1106+
struct attempt *attempt = new_attempt(payment, deliver, NULL);
1107+
return do_inject(aux_cmd, attempt);
1108+
}
1109+
10601110
req = jsonrpc_request_start(aux_cmd, "getroutes",
10611111
getroutes_done,
10621112
getroutes_done_err,
10631113
payment);
10641114

10651115
json_add_pubkey(req->js, "source", &xpay->local_id);
1066-
if (payment->paths)
1067-
json_add_pubkey(req->js, "destination", &xpay->fakenode);
1068-
else
1069-
json_add_pubkey(req->js, "destination", &payment->destination);
1116+
json_add_pubkey(req->js, "destination", dst);
1117+
10701118
payment_log(payment, LOG_DBG, "getroutes from %s to %s",
10711119
fmt_pubkey(tmpctx, &xpay->local_id),
10721120
payment->paths

tests/test_xpay.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,17 @@ def test_xpay_simple(node_factory):
200200
l1.rpc.xpay(b11_paid)
201201

202202

203+
def test_xpay_selfpay(node_factory):
204+
l1 = node_factory.get_node(options={'experimental-offers': None})
205+
206+
b11 = l1.rpc.invoice(1000, "test_xpay_selfpay1", "test_xpay_selfpay1")['bolt11']
207+
offer = l1.rpc.offer('any')
208+
b12 = l1.rpc.fetchinvoice(offer['bolt12'], '1000msat')['invoice']
209+
210+
l1.rpc.xpay(b11)
211+
l1.rpc.xpay(b12)
212+
213+
203214
@pytest.mark.slow_test
204215
@unittest.skipIf(TEST_NETWORK != 'regtest', '29-way split for node 17 is too dusty on elements')
205216
def test_xpay_fake_channeld(node_factory, bitcoind, chainparams):

0 commit comments

Comments
 (0)