diff --git a/doc/lightning-sendonion.7 b/doc/lightning-sendonion.7 index 47f6a44a2964..260921fee390 100644 --- a/doc/lightning-sendonion.7 +++ b/doc/lightning-sendonion.7 @@ -3,7 +3,7 @@ lightning-sendonion - Send a payment with a custom onion packet .SH SYNOPSIS -\fBsendonion\fR \fIonion\fR \fIfirst_hop\fR \fIpayment_hash\fR [\fIlabel\fR] [\fIshared_secrets\fR] [\fIpartid\fR] [\fIbolt11\fR] +\fBsendonion\fR \fIonion\fR \fIfirst_hop\fR \fIpayment_hash\fR [\fIlabel\fR] [\fIshared_secrets\fR] [\fIpartid\fR] [\fIbolt11\fR] [\fImsatoshi\fR] .SH DESCRIPTION @@ -89,6 +89,10 @@ partial payments with the same \fIpayment_hash\fR\. The \fIbolt11\fR parameter, if provided, will be returned in \fIwaitsendpay\fR and \fIlistsendpays\fR results\. + +The \fImsatoshi\fR parameter is used to annotate the payment, and is returned by +\fIwaitsendpay\fR and \fIlistsendpays\fR\. + .SH RETURN VALUE On success, an object similar to the output of \fBsendpay\fR will be diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 28ca83977c16..cde69a1dbe19 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -4,7 +4,7 @@ lightning-sendonion -- Send a payment with a custom onion packet SYNOPSIS -------- -**sendonion** *onion* *first_hop* *payment_hash* \[*label*\] \[*shared_secrets*\] \[*partid*\] \[*bolt11*\] +**sendonion** *onion* *first_hop* *payment_hash* \[*label*\] \[*shared_secrets*\] \[*partid*\] \[*bolt11*\] \[*msatoshi*\] DESCRIPTION ----------- @@ -78,6 +78,9 @@ partial payments with the same *payment_hash*. The *bolt11* parameter, if provided, will be returned in *waitsendpay* and *listsendpays* results. +The *msatoshi* parameter is used to annotate the payment, and is returned by +*waitsendpay* and *listsendpays*. + RETURN VALUE ------------ diff --git a/lightningd/pay.c b/lightningd/pay.c index 5c3e87254799..f9dc40f5715e 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1179,6 +1179,7 @@ static struct command_result *json_sendonion(struct command *cmd, struct lightningd *ld = cmd->ld; const char *label, *b11str; struct secret *path_secrets; + struct amount_msat *msat; u64 *partid; if (!param(cmd, buffer, params, @@ -1189,6 +1190,7 @@ static struct command_result *json_sendonion(struct command *cmd, p_opt("shared_secrets", param_secrets_array, &path_secrets), p_opt_def("partid", param_u64, &partid, 0), p_opt("bolt11", param_string, &b11str), + p_opt_def("msatoshi", param_msat, &msat, AMOUNT_MSAT(0)), NULL)) return command_param_failed(); @@ -1201,7 +1203,7 @@ static struct command_result *json_sendonion(struct command *cmd, failcode); return send_payment_core(ld, cmd, payment_hash, *partid, - first_hop, AMOUNT_MSAT(0), AMOUNT_MSAT(0), + first_hop, *msat, AMOUNT_MSAT(0), label, b11str, &packet, NULL, NULL, NULL, path_secrets); } diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index cc43c421b5b5..965fff5cf4e3 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1029,6 +1029,7 @@ static struct command_result *payment_createonion_success(struct command *cmd, json_object_end(req->js); json_add_sha256(req->js, "payment_hash", p->payment_hash); + json_add_amount_msat_only(req->js, "msatoshi", p->amount); json_array_start(req->js, "shared_secrets"); secrets = p->createonion_response->shared_secrets; diff --git a/plugins/pay.c b/plugins/pay.c index 77e81703778d..f7c9595a47a2 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1661,6 +1661,11 @@ struct pay_mpp { size_t num_nonfailed_parts; /* Total amount sent ("complete" or "pending" only). */ struct amount_msat amount_sent; + + /* Total amount received by the recipient ("complete" or "pending" + * only). Null if we have any part for which we didn't know the + * amount. */ + struct amount_msat *amount; }; static const struct sha256 *pay_mpp_key(const struct pay_mpp *pm) @@ -1683,17 +1688,43 @@ HTABLE_DEFINE_TYPE(struct pay_mpp, pay_mpp_key, pay_mpp_hash, pay_mpp_eq, static void add_amount_sent(struct plugin *p, const char *b11, - struct amount_msat *total, + struct pay_mpp *mpp, const char *buf, const jsmntok_t *t) { - struct amount_msat sent; + struct amount_msat sent, recv; + const jsmntok_t *msattok; + + json_to_msat(buf, json_get_member(buf, t, "amount_sent_msat"), &sent); - if (!amount_msat_add(total, *total, sent)) + if (!amount_msat_add(&mpp->amount_sent, mpp->amount_sent, sent)) plugin_log(p, LOG_BROKEN, "Cannot add amount_sent_msat for %s: %s + %s", b11, - type_to_string(tmpctx, struct amount_msat, total), + type_to_string(tmpctx, struct amount_msat, &mpp->amount_sent), + type_to_string(tmpctx, struct amount_msat, &sent)); + + msattok = json_get_member(buf, t, "amount_msat"); + + /* If this is an unannotated partial payment we drop out estimate for + * all parts. */ + if (msattok == NULL) { + mpp->amount = tal_free(mpp->amount); + return; + } + + /* If we had a part of this multi-part payment for which we don't know + * the amount, then this is NULL. No point in summing up if we don't + * have the exact value.*/ + if (mpp->amount == NULL) + return; + + json_to_msat(buf, msattok, &recv); + if (!amount_msat_add(mpp->amount, *mpp->amount, recv)) + plugin_log(p, LOG_BROKEN, + "Cannot add amount_msat for %s: %s + %s", + b11, + type_to_string(tmpctx, struct amount_msat, mpp->amount), type_to_string(tmpctx, struct amount_msat, &sent)); } @@ -1708,6 +1739,13 @@ static void add_new_entry(struct json_stream *ret, json_add_tok(ret, "label", pm->label, buf); if (pm->preimage) json_add_tok(ret, "preimage", pm->preimage, buf); + + /* This is only tallied for pending and successful payments, not + * failures. */ + if (pm->amount != NULL && pm->num_nonfailed_parts > 0) + json_add_string(ret, "amount_msat", + fmt_amount_msat(tmpctx, pm->amount)); + json_add_string(ret, "amount_sent_msat", fmt_amount_msat(tmpctx, &pm->amount_sent)); @@ -1759,6 +1797,7 @@ static struct command_result *listsendpays_done(struct command *cmd, pm->label = json_get_member(buf, t, "label"); pm->preimage = NULL; pm->amount_sent = AMOUNT_MSAT(0); + pm->amount = talz(pm, struct amount_msat); pm->num_nonfailed_parts = 0; pm->status = NULL; pay_map_add(&pay_map, pm); @@ -1766,15 +1805,13 @@ static struct command_result *listsendpays_done(struct command *cmd, status = json_get_member(buf, t, "status"); if (json_tok_streq(buf, status, "complete")) { - add_amount_sent(cmd->plugin, pm->b11, - &pm->amount_sent, buf, t); + add_amount_sent(cmd->plugin, pm->b11, pm, buf, t); pm->num_nonfailed_parts++; pm->status = "complete"; pm->preimage = json_get_member(buf, t, "payment_preimage"); } else if (json_tok_streq(buf, status, "pending")) { - add_amount_sent(cmd->plugin, pm->b11, - &pm->amount_sent, buf, t); + add_amount_sent(cmd->plugin, pm->b11, pm, buf, t); pm->num_nonfailed_parts++; /* Failed -> pending; don't downgrade success. */ if (!pm->status || !streq(pm->status, "complete")) diff --git a/tests/test_pay.py b/tests/test_pay.py index 33f2b4768417..3b787d64e3f2 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3211,8 +3211,10 @@ def test_bolt11_null_after_pay(node_factory, bitcoind): bitcoind.generate_block(6) sync_blockheight(bitcoind, [l1, l2]) - invl1 = l1.rpc.invoice(Millisatoshi(amount_sat * 2 * 1000), 'j', 'j')['bolt11'] + amt = Millisatoshi(amount_sat * 2 * 1000) + invl1 = l1.rpc.invoice(amt, 'j', 'j')['bolt11'] l2.rpc.pay(invl1) pays = l2.rpc.listpays()["pays"] - assert pays[0]["bolt11"] == invl1 + assert(pays[0]["bolt11"] == invl1) + assert('amount_msat' in pays[0] and pays[0]['amount_msat'] == amt) diff --git a/wallet/wallet.c b/wallet/wallet.c index 92ff7b8b6cbe..bbbe43644ab3 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2749,8 +2749,16 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, else payment->failonion = NULL; - db_column_amount_msat(stmt, 14, &payment->total_msat); - payment->partid = db_column_u64(stmt, 15); + if (!db_column_is_null(stmt, 14)) + db_column_amount_msat(stmt, 14, &payment->total_msat); + else + payment->total_msat = AMOUNT_MSAT(0); + + if (!db_column_is_null(stmt, 15)) + payment->partid = db_column_u64(stmt, 15); + else + payment->partid = 0; + return payment; }