diff --git a/lightningd/pay.c b/lightningd/pay.c index 87dc46be6..17cdfd0b9 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -2,6 +2,10 @@ #include #include #include +#if EXPERIMENTAL_FEATURES +#include +#include +#endif #include #include #include @@ -151,8 +155,14 @@ void json_add_payment_fields(struct json_stream *response, t->payment_preimage); if (t->label) json_add_string(response, "label", t->label); - if (t->bolt11) - json_add_string(response, "bolt11", t->bolt11); + if (t->invstring) { +#if EXPERIMENTAL_FEATURES + if (strstarts(t->invstring, "lni")) + json_add_string(response, "bolt12", t->invstring); + else +#endif + json_add_string(response, "bolt11", t->invstring); + } if (t->failonion) json_add_hex(response, "erroronion", t->failonion, @@ -858,7 +868,7 @@ send_payment_core(struct lightningd *ld, struct amount_msat msat, struct amount_msat total_msat, const char *label TAKES, - const char *b11str TAKES, + const char *invstring TAKES, const struct onionpacket *packet, const struct node_id *destination, struct node_id *route_nodes TAKES, @@ -1078,10 +1088,10 @@ send_payment_core(struct lightningd *ld, payment->label = tal_strdup(payment, label); else payment->label = NULL; - if (b11str != NULL) - payment->bolt11 = tal_strdup(payment, b11str); + if (invstring != NULL) + payment->invstring = tal_strdup(payment, invstring); else - payment->bolt11 = NULL; + payment->invstring = NULL; if (local_offer_id) payment->local_offer_id = tal_dup(payment, struct sha256, local_offer_id); else @@ -1103,7 +1113,7 @@ send_payment(struct lightningd *ld, struct amount_msat msat, struct amount_msat total_msat, const char *label TAKES, - const char *b11str TAKES, + const char *invstring TAKES, const struct sha256 *local_offer_id, const struct secret *payment_secret) { @@ -1186,7 +1196,7 @@ send_payment(struct lightningd *ld, n_hops, type_to_string(tmpctx, struct amount_msat, &msat)); packet = create_onionpacket(tmpctx, path, ROUTING_INFO_SIZE, &path_secrets); return send_payment_core(ld, cmd, rhash, partid, &route[0], - msat, total_msat, label, b11str, + msat, total_msat, label, invstring, packet, &ids[n_hops - 1], ids, channels, path_secrets, local_offer_id); } @@ -1268,7 +1278,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, *b11str; + const char *label, *invstring; struct node_id *destination; struct secret *path_secrets; struct amount_msat *msat; @@ -1282,7 +1292,8 @@ static struct command_result *json_sendonion(struct command *cmd, p_opt("label", param_escaped_string, &label), p_opt("shared_secrets", param_secrets_array, &path_secrets), p_opt_def("partid", param_u64, &partid, 0), - p_opt("bolt11", param_string, &b11str), + /* FIXME: paramter should be invstring now */ + p_opt("bolt11", param_string, &invstring), p_opt_def("msatoshi", param_msat, &msat, AMOUNT_MSAT(0)), p_opt("destination", param_node_id, &destination), #if EXPERIMENTAL_FEATURES @@ -1301,7 +1312,7 @@ static struct command_result *json_sendonion(struct command *cmd, return send_payment_core(ld, cmd, payment_hash, *partid, first_hop, *msat, AMOUNT_MSAT(0), - label, b11str, packet, destination, NULL, NULL, + label, invstring, packet, destination, NULL, NULL, path_secrets, local_offer_id); } @@ -1425,7 +1436,7 @@ static struct command_result *json_sendpay(struct command *cmd, struct sha256 *rhash; struct route_hop *route; struct amount_msat *msat; - const char *b11str, *label; + const char *invstring, *label; u64 *partid; struct secret *payment_secret; struct sha256 *local_offer_id = NULL; @@ -1436,7 +1447,8 @@ static struct command_result *json_sendpay(struct command *cmd, p_req("payment_hash", param_sha256, &rhash), p_opt("label", param_escaped_string, &label), p_opt("msatoshi", param_msat, &msat), - p_opt("bolt11", param_string, &b11str), + /* FIXME: paramter should be invstring now */ + p_opt("bolt11", param_string, &invstring), p_opt("payment_secret", param_secret, &payment_secret), p_opt_def("partid", param_u64, &partid, 0), #if EXPERIMENTAL_FEATURES @@ -1485,7 +1497,7 @@ static struct command_result *json_sendpay(struct command *cmd, route, final_amount, msat ? *msat : final_amount, - label, b11str, local_offer_id, payment_secret); + label, invstring, local_offer_id, payment_secret); } static const struct json_command sendpay_command = { @@ -1546,31 +1558,43 @@ static struct command_result *json_listsendpays(struct command *cmd, const struct wallet_payment **payments; struct json_stream *response; struct sha256 *rhash; - const char *b11str; + const char *invstring; if (!param(cmd, buffer, params, - p_opt("bolt11", param_string, &b11str), + /* FIXME: paramter should be invstring now */ + p_opt("bolt11", param_string, &invstring), p_opt("payment_hash", param_sha256, &rhash), NULL)) return command_param_failed(); - if (rhash && b11str) { + if (rhash && invstring) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Can only specify one of" " {bolt11} or {payment_hash}"); } - if (b11str) { + if (invstring) { struct bolt11 *b11; char *fail; - b11 = bolt11_decode(cmd, b11str, cmd->ld->our_features, NULL, + b11 = bolt11_decode(cmd, invstring, cmd->ld->our_features, NULL, chainparams, &fail); - if (!b11) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid bolt11: %s", fail); + if (b11) { + rhash = &b11->payment_hash; + } else { +#if EXPERIMENTAL_FEATURES + struct tlv_invoice *b12; + + b12 = invoice_decode(cmd, invstring, strlen(invstring), + cmd->ld->our_features, + chainparams, &fail); + if (b12 && b12->payment_hash) + rhash = b12->payment_hash; + else +#endif + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Invalid invstring: %s", fail); } - rhash = &b11->payment_hash; } payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash); diff --git a/plugins/keysend.c b/plugins/keysend.c index ed8f2a9ae..db5a57bb7 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -159,7 +159,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p->routes = NULL; p->min_final_cltv_expiry = DEFAULT_FINAL_CLTV_DELTA; p->features = NULL; - p->bolt11 = NULL; + p->invstring = NULL; p->why = "Initial attempt"; p->constraints.cltv_budget = *maxdelay; p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor)); diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 2e7f7695c..0eecfa5c1 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -57,7 +57,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->route = NULL; p->temp_exclusion = NULL; p->failroute_retry = false; - p->bolt11 = NULL; + p->invstring = NULL; p->routetxt = NULL; p->max_htlcs = UINT32_MAX; @@ -82,6 +82,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->features = parent->features; p->id = parent->id; p->local_id = parent->local_id; + p->local_offer_id = parent->local_offer_id; } else { assert(cmd != NULL); p->partid = 0; @@ -92,6 +93,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->id = next_id++; /* Caller must set this. */ p->local_id = NULL; + p->local_offer_id = NULL; } /* Initialize all modifier data so we can point to the fields when @@ -1443,12 +1445,16 @@ static struct command_result *payment_createonion_success(struct command *cmd, if (p->label) json_add_string(req->js, "label", p->label); - if (p->bolt11) - json_add_string(req->js, "bolt11", p->bolt11); + if (p->invstring) + /* FIXME: rename parameter to invstring */ + json_add_string(req->js, "bolt11", p->invstring); if (p->destination) json_add_node_id(req->js, "destination", p->destination); + if (p->local_offer_id) + json_add_sha256(req->js, "local_offer_id", p->local_offer_id); + send_outreq(p->plugin, req); return command_still_pending(cmd); } @@ -1805,8 +1811,8 @@ static void payment_finished(struct payment *p) json_add_string(ret, "failcodename", failure->failcodename); - if (p->bolt11) - json_add_string(ret, "bolt11", p->bolt11); + if (p->invstring) + json_add_invstring(ret, p->invstring); json_add_hex_talarr(ret, "raw_message", result.failure->raw_message); @@ -3265,7 +3271,7 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) /* Annotate the subpayments with the bolt11 string, * they'll be used when aggregating the payments * again. */ - c->bolt11 = tal_strdup(c, p->bolt11); + c->invstring = tal_strdup(c, p->invstring); /* 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 223651788..a25ae4c29 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -254,9 +254,13 @@ struct payment { * true. Set only on the root payment. */ bool abort; - /* Serialized bolt11 string, kept attachd to the root so we can filter + /* Serialized bolt11/12 string, kept attachd to the root so we can filter * by the invoice. */ - const char *bolt11; + const char *invstring; + + /* 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; /* Textual explanation of why this payment was attempted. */ const char *why; diff --git a/plugins/pay.c b/plugins/pay.c index f86cce093..86d726f4d 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -67,8 +67,8 @@ struct pay_status { struct amount_msat msat; /* CLTV delay required by destination. */ u32 final_cltv; - /* Bolt11 invoice. */ - const char *bolt11; + /* Bolt11/bolt12 invoice. */ + const char *invstring; /* What we did about routehints (if anything) */ const char *routehint_modifications; @@ -850,7 +850,7 @@ static struct command_result *getroute_done(struct command *cmd, sendpay_done, sendpay_error, pc); json_add_jsonstr(req->js, "route", attempt->route); json_add_string(req->js, "payment_hash", pc->payment_hash); - json_add_string(req->js, "bolt11", pc->ps->bolt11); + json_add_string(req->js, "bolt11", pc->ps->invstring); if (pc->label) json_add_string(req->js, "label", pc->label); if (pc->payment_secret) @@ -1255,7 +1255,7 @@ static struct route_info **filter_routehints(struct pay_command *pc, } static struct pay_status *add_pay_status(struct pay_command *pc, - const char *b11str) + const char *invstring STEALS) { struct pay_status *ps = tal(NULL, struct pay_status); @@ -1264,7 +1264,7 @@ static struct pay_status *add_pay_status(struct pay_command *pc, ps->label = tal_steal(ps, pc->label); ps->msat = pc->msat; ps->final_cltv = pc->final_cltv; - ps->bolt11 = tal_steal(ps, b11str); + ps->invstring = tal_steal(ps, invstring); ps->routehint_modifications = NULL; ps->shadow = NULL; ps->exclusions = NULL; @@ -1545,12 +1545,13 @@ static struct command_result *json_paystatus(struct command *cmd, const jsmntok_t *params) { struct pay_status *ps; - const char *b11str; + const char *invstring; struct json_stream *ret; struct payment *p; if (!param(cmd, buf, params, - p_opt("bolt11", param_string, &b11str), + /* FIXME: rename to invstring */ + p_opt("bolt11", param_string, &invstring), NULL)) return command_param_failed(); @@ -1560,11 +1561,11 @@ static struct command_result *json_paystatus(struct command *cmd, /* FIXME: Index by bolt11 string! */ /* TODO(cdecker) Remove once we migrated to `pay` with modifiers. */ list_for_each(&pay_status, ps, list) { - if (b11str && !streq(b11str, ps->bolt11)) + if (invstring && !streq(invstring, ps->invstring)) continue; json_object_start(ret, NULL); - json_add_string(ret, "bolt11", ps->bolt11); + json_add_invstring(ret, ps->invstring); json_add_u64(ret, "msatoshi", ps->msat.millisatoshis); /* Raw: JSON */ json_add_string(ret, "amount_msat", @@ -1591,15 +1592,15 @@ static struct command_result *json_paystatus(struct command *cmd, list_for_each(&payments, p, list) { assert(p->parent == NULL); - if (b11str && !streq(b11str, p->bolt11)) + if (invstring && !streq(invstring, p->invstring)) continue; json_object_start(ret, NULL); if (p->label != NULL) json_add_string(ret, "label", p->label); - if (p->bolt11) - json_add_string(ret, "bolt11", p->bolt11); + if (p->invstring) + json_add_invstring(ret, p->invstring); json_add_amount_msat_only(ret, "amount_msat", p->amount); json_add_string( ret, "amount_msat", @@ -1654,8 +1655,8 @@ struct pay_mpp { /* payment_hash from the invoice and lookup key */ const struct sha256 *payment_hash; - /* This is the bolt11 string */ - const char *b11; + /* This is the bolt11/bolt12 string */ + const char *invstring; /* Status of combined payment */ const char *status; /* Optional label (of first one!) */ @@ -1698,7 +1699,7 @@ HTABLE_DEFINE_TYPE(struct pay_mpp, pay_mpp_key, pay_mpp_hash, pay_mpp_eq, pay_map); static void add_amount_sent(struct plugin *p, - const char *b11, + const char *invstring, struct pay_mpp *mpp, const char *buf, const jsmntok_t *t) @@ -1711,7 +1712,7 @@ static void add_amount_sent(struct plugin *p, 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, + invstring, type_to_string(tmpctx, struct amount_msat, &mpp->amount_sent), type_to_string(tmpctx, struct amount_msat, &sent)); @@ -1734,7 +1735,7 @@ static void add_amount_sent(struct plugin *p, if (!amount_msat_add(mpp->amount, *mpp->amount, recv)) plugin_log(p, LOG_BROKEN, "Cannot add amount_msat for %s: %s + %s", - b11, + invstring, type_to_string(tmpctx, struct amount_msat, mpp->amount), type_to_string(tmpctx, struct amount_msat, &sent)); } @@ -1744,8 +1745,8 @@ static void add_new_entry(struct json_stream *ret, const struct pay_mpp *pm) { json_object_start(ret, NULL); - if (pm->b11) - json_add_string(ret, "bolt11", pm->b11); + if (pm->invstring) + json_add_invstring(ret, pm->invstring); if (pm->destination) json_add_tok(ret, "destination", pm->destination, buf); @@ -1777,7 +1778,7 @@ static void add_new_entry(struct json_stream *ret, static struct command_result *listsendpays_done(struct command *cmd, const char *buf, const jsmntok_t *result, - char *b11str) + char *invstring) { size_t i; const jsmntok_t *t, *arr; @@ -1794,12 +1795,16 @@ static struct command_result *listsendpays_done(struct command *cmd, "Unexpected non-array result from listsendpays"); json_for_each_arr(i, t, arr) { - const jsmntok_t *status, *b11tok, *hashtok, *createdtok; - const char *b11 = b11str; + const jsmntok_t *status, *invstrtok, *hashtok, *createdtok; + const char *invstr = invstring; struct sha256 payment_hash; u32 created_at; - b11tok = json_get_member(buf, t, "bolt11"); + invstrtok = json_get_member(buf, t, "bolt11"); +#if EXPERIMENTAL_FEATURES + if (!invstrtok) + invstrtok = json_get_member(buf, t, "bolt12"); +#endif hashtok = json_get_member(buf, t, "payment_hash"); createdtok = json_get_member(buf, t, "created_at"); assert(hashtok != NULL); @@ -1807,14 +1812,14 @@ static struct command_result *listsendpays_done(struct command *cmd, json_to_sha256(buf, hashtok, &payment_hash); json_to_u32(buf, createdtok, &created_at); - if (b11tok) - b11 = json_strdup(cmd, buf, b11tok); + if (invstrtok) + invstr = json_strdup(cmd, buf, invstrtok); pm = pay_map_get(&pay_map, &payment_hash); if (!pm) { pm = tal(cmd, struct pay_mpp); pm->payment_hash = tal_dup(pm, struct sha256, &payment_hash); - pm->b11 = tal_steal(pm, b11); + pm->invstring = tal_steal(pm, invstr); pm->destination = json_get_member(buf, t, "destination"); pm->label = json_get_member(buf, t, "label"); pm->preimage = NULL; @@ -1828,13 +1833,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, buf, t); + add_amount_sent(cmd->plugin, pm->invstring, 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, buf, t); + add_amount_sent(cmd->plugin, pm->invstring, pm, buf, t); pm->num_nonfailed_parts++; /* Failed -> pending; don't downgrade success. */ if (!pm->status || !streq(pm->status, "complete")) @@ -1870,22 +1875,23 @@ static struct command_result *json_listpays(struct command *cmd, const char *buf, const jsmntok_t *params) { - const char *b11str; + const char *invstring; struct sha256 *payment_hash; struct out_req *req; /* FIXME: would be nice to parse as a bolt11 so check worked in future */ if (!param(cmd, buf, params, - p_opt("bolt11", param_string, &b11str), + /* FIXME: paramter should be invstring now */ + p_opt("bolt11", param_string, &invstring), p_opt("payment_hash", param_sha256, &payment_hash), NULL)) return command_param_failed(); req = jsonrpc_request_start(cmd->plugin, cmd, "listsendpays", listsendpays_done, forward_error, - cast_const(char *, b11str)); - if (b11str) - json_add_string(req->js, "bolt11", b11str); + cast_const(char *, invstring)); + if (invstring) + json_add_string(req->js, "bolt11", invstring); if (payment_hash) json_add_sha256(req->js, "payment_hash", payment_hash); @@ -1956,6 +1962,9 @@ static struct command_result *json_paymod(struct command *cmd, unsigned int *retryfor; u64 *riskfactor_millionths; struct shadow_route_data *shadow_route; +#if EXPERIMENTAL_FEATURES + struct sha256 *local_offer_id; +#endif #if DEVELOPER bool *use_shadow; #endif @@ -1974,6 +1983,9 @@ static struct command_result *json_paymod(struct command *cmd, p_opt_def("maxdelay", param_number, &maxdelay, maxdelay_default), p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), +#if EXPERIMENTAL_FEATURES + p_opt("local_offer_id", param_sha256, &local_offer_id), +#endif #if DEVELOPER p_opt_def("use_shadow", param_bool, &use_shadow, true), #endif @@ -2031,7 +2043,7 @@ static struct command_result *json_paymod(struct command *cmd, p->routes = tal_steal(p, b11->routes); p->min_final_cltv_expiry = b11->min_final_cltv_expiry; p->features = tal_steal(p, b11->features); - p->bolt11 = tal_steal(p, b11str); + p->invstring = tal_steal(p, b11str); p->why = "Initial attempt"; p->constraints.cltv_budget = *maxdelay; p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor)); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 4f3d95e07..18e134215 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -387,6 +387,8 @@ def test_pay_plugin(node_factory): # Make sure usage messages are present. msg = 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] '\ '[retry_for] [maxdelay] [exemptfee]' + if EXPERIMENTAL_FEATURES: + msg += ' [local_offer_id]' if DEVELOPER: msg += ' [use_shadow]' assert only_one(l1.rpc.help('pay')['help'])['command'] == msg diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 2fcd3d5a8..8ce9b6f18 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1762,4 +1762,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:93d29f78f9f38cc779f4fbea480b90ce38899ff1c2f534e4160b2bf8a05b57ee +// SHA256STAMP:774d5116e102d98351730072b4aa0b48d27b05c364680fc406f8b12bf4c7e293 diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 697c0d561..2b83c78c5 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1762,4 +1762,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:93d29f78f9f38cc779f4fbea480b90ce38899ff1c2f534e4160b2bf8a05b57ee +// SHA256STAMP:774d5116e102d98351730072b4aa0b48d27b05c364680fc406f8b12bf4c7e293 diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index d841d81c7..2750c17fc 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1161,4 +1161,4 @@ msgstr "" #: wallet/test/run-wallet.c:1378 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:f567e217e4f94d8fd86c0c8d6997931d891df4f8295517fa527b80a323b256a8 +# SHA256STAMP:50622dd1a8b0f2fe71efa1c1d175f7ad130f3042db84e0c9547f849a328e8f5d diff --git a/wallet/wallet.c b/wallet/wallet.c index a0ed72b7f..ac6abc056 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2530,8 +2530,8 @@ void wallet_payment_store(struct wallet *wallet, else db_bind_null(stmt, 9); - if (payment->bolt11 != NULL) - db_bind_text(stmt, 10, payment->bolt11); + if (payment->invstring != NULL) + db_bind_text(stmt, 10, payment->invstring); else db_bind_null(stmt, 10); @@ -2641,10 +2641,10 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, payment->label = NULL; if (!db_column_is_null(stmt, 12) && db_column_text(stmt, 12) != NULL) - payment->bolt11 = tal_strdup( + payment->invstring = tal_strdup( payment, (const char *)db_column_text(stmt, 12)); else - payment->bolt11 = NULL; + payment->invstring = NULL; if (!db_column_is_null(stmt, 13)) payment->failonion = diff --git a/wallet/wallet.h b/wallet/wallet.h index 0afe24c7e..cfbced10f 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -236,8 +236,8 @@ struct wallet_payment { struct secret *path_secrets; struct node_id *route_nodes; struct short_channel_id *route_channels; - /* bolt11 string; NULL for old payments. */ - const char *bolt11; + /* bolt11/bolt12 string; NULL for old payments. */ + const char *invstring; /* The label of the payment. Must support `tal_len` */ const char *label;