diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index f4c2edf82..8f098ec98 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -24,6 +24,7 @@ On success, an object containing **pays** is returned. It is an array of object - **destination** (pubkey, optional): the final destination of the payment if known - **label** (string, optional): the label, if given to sendpay - **bolt11** (string, optional): the bolt11 string (if pay supplied one) +- **description** (string, optional): the description matching the bolt11 description hash (if pay supplied one) - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "pending" or "complete": @@ -56,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6ffbb1273de04f356cf79dab9a988ab030eee3317cb22e10d12d1c672249fc67) +[comment]: # ( SHA256STAMP:3c158259410ff8eb81669e26eca9ee53017002d739f89e7f0e2fd8e61edb8a14) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 13d82b8f4..c2511d09f 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -34,6 +34,7 @@ On success, an object containing **payments** is returned. It is an array of ob - **destination** (pubkey, optional): the final destination of the payment if known - **label** (string, optional): the label, if given to sendpay - **bolt11** (string, optional): the bolt11 string (if pay supplied one) +- **description** (string, optional): the description matching the bolt11 description hash (if pay supplied one) - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "complete": @@ -60,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b03c2f306bafb1919f0933ebc695657bd691591484ddcb39b1e8706335593cd2) +[comment]: # ( SHA256STAMP:eaa0b4c6309d45bc2a72baf44288f1faa75d7f6ff2e8bf6d03be53747fe82c84) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 6ecb18062..f332e812f 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -5,7 +5,8 @@ SYNOPSIS -------- **sendpay** *route* *payment\_hash* [*label*] [*msatoshi*] -[*bolt11*] [*payment_secret*] [*partid*] [*localofferid*] [*groupid*] [*payment_metadata*] +[*bolt11*] [*payment_secret*] [*partid*] [*localofferid*] [*groupid*] +[*payment_metadata*] [*description*] DESCRIPTION ----------- diff --git a/doc/schemas/listpays.schema.json b/doc/schemas/listpays.schema.json index f10062ea2..ad74829ab 100644 --- a/doc/schemas/listpays.schema.json +++ b/doc/schemas/listpays.schema.json @@ -48,6 +48,10 @@ "type": "string", "description": "the bolt11 string (if pay supplied one)" }, + "description": { + "type": "string", + "description": "the description matching the bolt11 description hash (if pay supplied one)" + }, "bolt12": { "type": "string", "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." @@ -78,6 +82,7 @@ "created_at": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "preimage": {}, "number_of_parts": {}, @@ -115,6 +120,7 @@ "created_at": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "amount_msat": {}, "amount_sent_msat": {}, @@ -152,6 +158,7 @@ "created_at": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "amount_sent_msat": {}, "erroronion": { diff --git a/doc/schemas/listsendpays.schema.json b/doc/schemas/listsendpays.schema.json index 8db7f553f..3a45a3b7d 100644 --- a/doc/schemas/listsendpays.schema.json +++ b/doc/schemas/listsendpays.schema.json @@ -72,6 +72,10 @@ "type": "string", "description": "the bolt11 string (if pay supplied one)" }, + "description": { + "type": "string", + "description": "the description matching the bolt11 description hash (if pay supplied one)" + }, "bolt12": { "type": "string", "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." @@ -108,6 +112,7 @@ "amount_sent_msat": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "payment_preimage": { "type": "secret", @@ -146,6 +151,7 @@ "amount_sent_msat": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "erroronion": { "type": "hex", @@ -182,6 +188,7 @@ "amount_sent_msat": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {} } } diff --git a/lightningd/pay.c b/lightningd/pay.c index 9e29f92d0..0f0745786 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -154,6 +154,8 @@ void json_add_payment_fields(struct json_stream *response, else json_add_string(response, "bolt11", t->invstring); } + if (t->description) + json_add_string(response, "description", t->description); if (t->failonion) json_add_hex(response, "erroronion", t->failonion, @@ -857,6 +859,7 @@ send_payment_core(struct lightningd *ld, struct amount_msat total_msat, const char *label TAKES, const char *invstring TAKES, + const char *description TAKES, const struct onionpacket *packet, const struct node_id *destination, struct node_id *route_nodes TAKES, @@ -1100,6 +1103,10 @@ send_payment_core(struct lightningd *ld, payment->invstring = tal_strdup(payment, invstring); else payment->invstring = NULL; + if (description != NULL) + payment->description = tal_strdup(payment, description); + else + payment->description = NULL; payment->local_offer_id = tal_dup_or_null(payment, struct sha256, local_offer_id); @@ -1121,6 +1128,7 @@ send_payment(struct lightningd *ld, struct amount_msat total_msat, const char *label TAKES, const char *invstring TAKES, + const char *description TAKES, const struct sha256 *local_offer_id, const struct secret *payment_secret, const u8 *payment_metadata) @@ -1194,7 +1202,7 @@ send_payment(struct lightningd *ld, packet = create_onionpacket(tmpctx, path, ROUTING_INFO_SIZE, &path_secrets); return send_payment_core(ld, cmd, rhash, partid, group, &route[0], msat, total_msat, - label, invstring, + label, invstring, description, packet, &ids[n_hops - 1], ids, channels, path_secrets, local_offer_id); } @@ -1265,7 +1273,7 @@ static struct command_result *json_sendonion(struct command *cmd, struct route_hop *first_hop; struct sha256 *payment_hash; struct lightningd *ld = cmd->ld; - const char *label, *invstring; + const char *label, *invstring, *description; struct node_id *destination; struct secret *path_secrets; struct amount_msat *msat; @@ -1285,6 +1293,7 @@ static struct command_result *json_sendonion(struct command *cmd, p_opt("destination", param_node_id, &destination), p_opt("localofferid", param_sha256, &local_offer_id), p_opt("groupid", param_u64, &group), + p_opt("description", param_string, &description), NULL)) return command_param_failed(); @@ -1306,7 +1315,8 @@ static struct command_result *json_sendonion(struct command *cmd, return send_payment_core(ld, cmd, payment_hash, *partid, *group, first_hop, *msat, AMOUNT_MSAT(0), - label, invstring, packet, destination, NULL, NULL, + label, invstring, description, + packet, destination, NULL, NULL, path_secrets, local_offer_id); } @@ -1420,7 +1430,7 @@ static struct command_result *json_sendpay(struct command *cmd, struct sha256 *rhash; struct route_hop *route; struct amount_msat *msat; - const char *invstring, *label; + const char *invstring, *label, *description; u64 *partid, *group; struct secret *payment_secret; struct sha256 *local_offer_id; @@ -1439,6 +1449,7 @@ static struct command_result *json_sendpay(struct command *cmd, p_opt("localofferid", param_sha256, &local_offer_id), p_opt("groupid", param_u64, &group), p_opt("payment_metadata", param_bin_from_hex, &payment_metadata), + p_opt("description", param_string, &description), NULL)) return command_param_failed(); @@ -1488,7 +1499,7 @@ static struct command_result *json_sendpay(struct command *cmd, route, final_amount, msat ? *msat : final_amount, - label, invstring, local_offer_id, + label, invstring, description, local_offer_id, payment_secret, payment_metadata); } diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 1c8779f41..b206ce759 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -65,6 +65,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->temp_exclusion = NULL; p->failroute_retry = false; p->invstring = NULL; + p->description = NULL; p->routetxt = NULL; p->max_htlcs = UINT32_MAX; p->aborterror = NULL; @@ -1568,6 +1569,9 @@ static struct command_result *payment_createonion_success(struct command *cmd, /* FIXME: rename parameter to invstring */ json_add_string(req->js, "bolt11", p->invstring); + if (p->description) + json_add_string(req->js, "description", p->description); + if (p->destination) json_add_node_id(req->js, "destination", p->destination); @@ -3559,6 +3563,8 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) * they'll be used when aggregating the payments * again. */ c->invstring = tal_strdup(c, p->invstring); + if (p->description) + c->description = tal_strdup(c, p->description); /* Get ~ target, but don't exceed amt */ c->amount = fuzzed_near(target, amt); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 86aea238c..220972547 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -259,6 +259,9 @@ struct payment { * by the invoice. */ const char *invstring; + /* Description, usually set if bolt11 has only description_hash */ + const char *description; + /* If this is paying a local offer, this is the one (sendpay ensures we * don't pay twice for single-use offers) */ struct sha256 *local_offer_id; diff --git a/plugins/pay.c b/plugins/pay.c index 7cebac54c..610d369c1 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -273,6 +273,8 @@ struct pay_mpp { /* Optional label (of first one!) */ const jsmntok_t *label; + /* Optional description (used for bolt11 with description_hash) */ + const jsmntok_t *description; /* Optional preimage (iff status is successful) */ const jsmntok_t *preimage; /* Only counts "complete" or "pending" payments. */ @@ -373,7 +375,8 @@ static void add_new_entry(struct json_stream *ret, json_object_start(ret, NULL); if (pm->invstring) json_add_invstring(ret, pm->invstring); - + if (pm->description) + json_add_tok(ret, "description", pm->description, buf); if (pm->destination) json_add_tok(ret, "destination", pm->destination, buf); @@ -465,6 +468,7 @@ static struct command_result *listsendpays_done(struct command *cmd, pm->invstring = tal_steal(pm, invstr); pm->destination = json_get_member(buf, t, "destination"); pm->label = json_get_member(buf, t, "label"); + pm->description = json_get_member(buf, t, "description"); pm->preimage = NULL; pm->amount_sent = AMOUNT_MSAT(0); pm->amount = talz(pm, struct amount_msat); @@ -968,6 +972,7 @@ static struct command_result *json_pay(struct command *cmd, p = payment_new(cmd, cmd, NULL /* No parent */, paymod_mods); p->invstring = tal_steal(p, b11str); + p->description = tal_steal(p, description); if (!bolt12_has_prefix(b11str)) { b11 = diff --git a/tests/test_invoices.py b/tests/test_invoices.py index ba66a2efe..8f4aa6b5c 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -716,6 +716,16 @@ def test_invoice_deschash(node_factory, chainparams): l1.rpc.pay(inv['bolt11'], description=listinv['description']) + # Description will be in some. + found = False + for p in l1.rpc.listsendpays()['payments']: + if 'description' in p: + found = True + assert p['description'] == listinv['description'] + assert found + + assert only_one(l1.rpc.listpays(inv['bolt11'])['pays'])['description'] == listinv['description'] + # Try removing description. l2.rpc.delinvoice('label', "paid", desconly=True) assert 'description' not in only_one(l2.rpc.listinvoices()['invoices']) diff --git a/wallet/db.c b/wallet/db.c index 0d086fea0..04497400d 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -873,6 +873,8 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channels ADD htlc_maximum_msat BIGINT DEFAULT 2100000000000000"), NULL}, {SQL("ALTER TABLE channels ADD htlc_minimum_msat BIGINT DEFAULT 0"), NULL}, {SQL("ALTER TABLE forwarded_payments ADD forward_style INTEGER DEFAULT NULL"), NULL}, + /* "description" is used for label, so we use "paydescription" here */ + {SQL("ALTER TABLE payments ADD paydescription TEXT;"), NULL}, }; /** diff --git a/wallet/wallet.c b/wallet/wallet.c index bf0710b0f..da3fa96d0 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3033,8 +3033,9 @@ void wallet_payment_store(struct wallet *wallet, " total_msat," " partid," " local_offer_id," - " groupid" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " groupid," + " paydescription" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_int(stmt, 0, payment->status); db_bind_sha256(stmt, 1, &payment->payment_hash); @@ -3083,6 +3084,11 @@ void wallet_payment_store(struct wallet *wallet, db_bind_u64(stmt, 14, payment->groupid); + if (payment->description != NULL) + db_bind_text(stmt, 15, payment->description); + else + db_bind_null(stmt, 15); + db_exec_prepared_v2(stmt); payment->id = db_last_insert_id_v2(stmt); assert(payment->id > 0); @@ -3177,6 +3183,11 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, else payment->label = NULL; + if (!db_col_is_null(stmt, "paydescription")) + payment->description = db_col_strdup(payment, stmt, "paydescription"); + else + payment->description = NULL; + if (!db_col_is_null(stmt, "bolt11")) payment->invstring = db_col_strdup(payment, stmt, "bolt11"); else @@ -3236,6 +3247,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, ", msatoshi_sent" ", description" ", bolt11" + ", paydescription" ", failonionreply" ", total_msat" ", partid" @@ -3472,6 +3484,7 @@ wallet_payment_list(const tal_t *ctx, ", msatoshi_sent" ", description" ", bolt11" + ", paydescription" ", failonionreply" ", total_msat" ", partid" @@ -3538,6 +3551,7 @@ wallet_payments_by_offer(const tal_t *ctx, ", msatoshi_sent" ", description" ", bolt11" + ", paydescription" ", failonionreply" ", total_msat" ", partid" diff --git a/wallet/wallet.h b/wallet/wallet.h index 2c179b465..4003adc74 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -358,6 +358,9 @@ struct wallet_payment { /* The label of the payment. Must support `tal_len` */ const char *label; + /* The description of the payment (used if invstring has hash). */ + const char *description; + /* If we could not decode the fail onion, just add it here. */ const u8 *failonion;