diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 4b087a73a190..f7d82b12812d 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -98,11 +98,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, { struct chain_coin_mvt *mvt = tal(ctx, struct chain_coin_mvt); - if (account_name) - mvt->account_name = tal_strdup(mvt, account_name); - else - mvt->account_name = NULL; - + mvt->account_name = tal_strdup_or_null(mvt, account_name); mvt->tx_txid = tx_txid; mvt->outpoint = outpoint; mvt->originating_acct = NULL; @@ -393,11 +389,8 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, struct coin_mvt *mvt = tal(ctx, struct coin_mvt); mvt->account_id = tal_strdup(mvt, chain_mvt->account_name); - if (chain_mvt->originating_acct) - mvt->originating_acct = - tal_strdup(mvt, chain_mvt->originating_acct); - else - mvt->originating_acct = NULL; + mvt->originating_acct = + tal_strdup_or_null(mvt, chain_mvt->originating_acct); mvt->hrp_name = tal_strdup(mvt, hrp_name); mvt->type = CHAIN_MVT; diff --git a/common/configvar.c b/common/configvar.c index 3aae4b0ba09f..84ddb164689f 100644 --- a/common/configvar.c +++ b/common/configvar.c @@ -13,10 +13,7 @@ struct configvar *configvar_new(const tal_t *ctx, const char *configline) { struct configvar *cv = tal(ctx, struct configvar); - if (file) - cv->file = tal_strdup(cv, file); - else - cv->file = NULL; + cv->file = tal_strdup_or_null(cv, file); cv->src = src; cv->linenum = linenum; cv->configline = tal_strdup(cv, configline); diff --git a/common/utils.c b/common/utils.c index 165a1d5569b7..0c9ecc5ae270 100644 --- a/common/utils.c +++ b/common/utils.c @@ -148,6 +148,16 @@ char *utf8_str(const tal_t *ctx, const u8 *buf TAKES, size_t buflen) return ret; } +char *tal_strdup_or_null(const tal_t *ctx, const char *str) +{ + if (!str) { + /* You might have taken NULL; that's legal! Release now. */ + taken(str); + return NULL; + } + return tal_strdup(ctx, str); +} + int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created) { char *tmpdir = getenv("TMPDIR"); diff --git a/common/utils.h b/common/utils.h index e1793bdeab34..cc49aa3b5a72 100644 --- a/common/utils.h +++ b/common/utils.h @@ -88,6 +88,9 @@ bool utf8_check(const void *buf, size_t buflen); /* Check it's UTF-8, return copy (or same if TAKES), or NULL if not valid. */ char *utf8_str(const tal_t *ctx, const u8 *buf TAKES, size_t buflen); +/* Strdup, or pass through NULL */ +char *tal_strdup_or_null(const tal_t *ctx, const char *str); + /* Use the POSIX C locale. */ void setup_locale(void); diff --git a/db/bindings.c b/db/bindings.c index 37aac237ed34..aa9795161a56 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -319,6 +319,17 @@ char *db_col_strdup(const tal_t *ctx, return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); } +char *db_col_strdup_optional(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + if (db_column_is_null(stmt, col)) + return NULL; + + return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); +} + void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage) { @@ -346,17 +357,22 @@ void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *d memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); } +/* We don't assume sizeof(struct node_id) == sizeof(struct node_id.k), + * otherwise this would simply be a call to db_col_arr! + * Thanks ARM! */ struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, - const char *colname) + const char *colname) { size_t col = db_query_colnum(stmt, colname); struct node_id *ret; size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); const u8 *arr = db_column_blob(stmt, col); assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); - ret = tal_arr(ctx, struct node_id, n); - db_column_null_warn(stmt, colname, col); + if (db_column_is_null(stmt, col)) + return NULL; + + ret = tal_arr(ctx, struct node_id, n); for (size_t i = 0; i < n; i++) memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); @@ -399,7 +415,9 @@ db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char * size_t len; struct short_channel_id *ret; - db_column_null_warn(stmt, colname, col); + if (db_column_is_null(stmt, col)) + return NULL; + ser = db_column_blob(stmt, col); len = db_column_bytes(stmt, col); ret = tal_arr(ctx, struct short_channel_id, 0); @@ -516,15 +534,14 @@ void db_col_amount_msat_or_default(struct db_stmt *stmt, msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ } -void db_col_amount_msat(struct db_stmt *stmt, const char *colname, - struct amount_msat *msat) +struct amount_msat db_col_amount_msat(struct db_stmt *stmt, const char *colname) { - msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ + return amount_msat(db_col_u64(stmt, colname)); } -void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat) +struct amount_sat db_col_amount_sat(struct db_stmt *stmt, const char *colname) { - sat->satoshis = db_col_u64(stmt, colname); /* Raw: low level function */ + return amount_sat(db_col_u64(stmt, colname)); } struct json_escape *db_col_json_escape(const tal_t *ctx, diff --git a/db/bindings.h b/db/bindings.h index 66da63da5f69..968889687e3e 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -67,9 +67,13 @@ const void* db_col_blob(struct db_stmt *stmt, const char *colname); char *db_col_strdup(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +/* string or NULL */ +char *db_col_strdup_optional(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname); void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage); -void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat); -void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat); +struct amount_msat db_col_amount_msat(struct db_stmt *stmt, const char *colname); +struct amount_sat db_col_amount_sat(struct db_stmt *stmt, const char *colname); struct json_escape *db_col_json_escape(const tal_t *ctx, struct db_stmt *stmt, const char *colname); void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha); void db_col_sha256d(struct db_stmt *stmt, const char *colname, struct sha256_double *shad); diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index c0a02dff99f4..1fae8d5bf657 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -119,7 +119,7 @@ The following error codes may occur: - 201: Already paid with this *hash* using different amount or destination. - 203: Permanent failure at destination. The *data* field of the error -will be routing failure object. +will be routing failure object (except for self-payment, which currently returns the error directly from lightning-sendpay(7)). - 205: Unable to find a route. - 206: Route too expensive. Either the fee or the needed total locktime for the route exceeds your *maxfeepercent* or *maxdelay* diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 855c4968dd84..287411799f3f 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -17,18 +17,19 @@ route. Generally, a client would call lightning-getroute(7) to resolve a route, then use **sendpay** to send it. If it fails, it would call -lightning-getroute(7) again to retry. +lightning-getroute(7) again to retry. If the route is empty, a payment-to-self is attempted. The response will occur when the payment is on its way to the destination. The **sendpay** RPC command does not wait for definite -success or definite failure of the payment. Instead, use the +success or definite failure of the payment (except for already-succeeded +payments, or to-self payments). Instead, use the **waitsendpay** RPC command to poll or wait for definite success or definite failure. The *label* and *bolt11* parameters, if provided, will be returned in *waitsendpay* and *listsendpays* results. -The *amount\_msat* amount must be provided if *partid* is non-zero, otherwise +The *amount\_msat* amount must be provided if *partid* is non-zero, or the payment is to-self, otherwise it must be equal to the final amount to the destination. By default it is in millisatoshi precision; it can be a whole number, or a whole number ending in *msat* or *sat*, or a number with three decimal places ending @@ -39,10 +40,10 @@ accept the payment, as defined by the `payment_data` field in BOLT 4 and the `s` field in the BOLT 11 invoice format. It is required if *partid* is non-zero. -The *partid* value, if provided and non-zero, allows for multiple parallel +The *partid* value must not be provided for self-payments. If provided and non-zero, allows for multiple parallel partial payments with the same *payment\_hash*. The *amount\_msat* amount (which must be provided) for each **sendpay** with matching -*payment\_hash* must be equal, and **sendpay** will fail if there are +*payment\_hash* must be equal, and **sendpay** will fail if there are differing values given. The *localinvreqid* value indicates that this payment is being made for a local invoice\_request: this ensures that we only send a payment for a single-use diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index c7ffa5bb399a..3c72da362dc8 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -186,8 +186,7 @@ static void rebroadcast_txs(struct chain_topology *topo) tal_arr_expand(&txs->txs, fmt_bitcoin_tx(txs->txs, otx->tx)); tal_arr_expand(&txs->allowhighfees, otx->allowhighfees); - tal_arr_expand(&txs->cmd_id, - otx->cmd_id ? tal_strdup(txs, otx->cmd_id) : NULL); + tal_arr_expand(&txs->cmd_id, tal_strdup_or_null(txs, otx->cmd_id)); } tal_free(cleanup_ctx); @@ -277,10 +276,7 @@ void broadcast_tx_(struct chain_topology *topo, otx->cbarg = cbarg; if (taken(otx->cbarg)) tal_steal(otx, otx->cbarg); - if (cmd_id) - otx->cmd_id = tal_strdup(otx, cmd_id); - else - otx->cmd_id = NULL; + otx->cmd_id = tal_strdup_or_null(otx, cmd_id); /* Note that if the minimum block is N, we broadcast it when * we have block N-1! */ diff --git a/lightningd/htlc_set.c b/lightningd/htlc_set.c index da6604628e46..402e6b83ff37 100644 --- a/lightningd/htlc_set.c +++ b/lightningd/htlc_set.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,7 @@ void htlc_set_add(struct lightningd *ld, { struct htlc_set *set; const struct invoice_details *details; + const char *err; /* BOLT #4: * The final node: @@ -109,8 +111,9 @@ void htlc_set_add(struct lightningd *ld, * - Note: "amount paid" specified there is the `total_msat` field. */ details = invoice_check_payment(tmpctx, ld, &hin->payment_hash, - total_msat, payment_secret); + total_msat, payment_secret, &err); if (!details) { + log_debug(hin->key.channel->log, "payment failed: %s", err); local_fail_in_htlc(hin, take(failmsg_incorrect_or_unknown(NULL, ld, hin))); return; diff --git a/lightningd/invoice.c b/lightningd/invoice.c index e73f40b787ef..42f4ef985d80 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -367,7 +367,8 @@ invoice_check_payment(const tal_t *ctx, struct lightningd *ld, const struct sha256 *payment_hash, const struct amount_msat msat, - const struct secret *payment_secret) + const struct secret *payment_secret, + const char **err) { u64 inv_dbid; const struct invoice_details *details; @@ -382,11 +383,12 @@ invoice_check_payment(const tal_t *ctx, * - MUST return an `incorrect_or_unknown_payment_details` error. */ if (!invoices_find_unpaid(ld->wallet->invoices, &inv_dbid, payment_hash)) { - log_debug(ld->log, "Unknown paid invoice %s", - type_to_string(tmpctx, struct sha256, payment_hash)); if (invoices_find_by_rhash(ld->wallet->invoices, &inv_dbid, payment_hash)) { - log_debug(ld->log, "ALREADY paid invoice %s", - type_to_string(tmpctx, struct sha256, payment_hash)); + *err = tal_fmt(ctx, "Already paid or expired invoice %s", + type_to_string(tmpctx, struct sha256, payment_hash)); + } else { + *err = tal_fmt(ctx, "Unknown invoice %s", + type_to_string(tmpctx, struct sha256, payment_hash)); } return NULL; } @@ -401,8 +403,8 @@ invoice_check_payment(const tal_t *ctx, */ if (feature_is_set(details->features, COMPULSORY_FEATURE(OPT_VAR_ONION)) && !payment_secret) { - log_debug(ld->log, "Attept to pay %s without secret", - type_to_string(tmpctx, struct sha256, &details->rhash)); + *err = tal_fmt(ctx, "Attempt to pay %s without secret", + type_to_string(tmpctx, struct sha256, &details->rhash)); return tal_free(details); } @@ -414,9 +416,9 @@ invoice_check_payment(const tal_t *ctx, else invoice_secret(&details->r, &expected); if (!secret_eq_consttime(payment_secret, &expected)) { - log_debug(ld->log, "Attept to pay %s with wrong secret", - type_to_string(tmpctx, struct sha256, - &details->rhash)); + *err = tal_fmt(ctx, "Attempt to pay %s with wrong secret", + type_to_string(tmpctx, struct sha256, + &details->rhash)); return tal_free(details); } } @@ -432,21 +434,21 @@ invoice_check_payment(const tal_t *ctx, struct amount_msat twice; if (amount_msat_less(msat, *details->msat)) { - log_debug(ld->log, "Attept to pay %s with amount %s < %s", - type_to_string(tmpctx, struct sha256, - &details->rhash), - type_to_string(tmpctx, struct amount_msat, &msat), - type_to_string(tmpctx, struct amount_msat, details->msat)); + *err = tal_fmt(ctx, "Attempt to pay %s with amount %s < %s", + type_to_string(tmpctx, struct sha256, + &details->rhash), + type_to_string(tmpctx, struct amount_msat, &msat), + type_to_string(tmpctx, struct amount_msat, details->msat)); return tal_free(details); } if (amount_msat_add(&twice, *details->msat, *details->msat) && amount_msat_greater(msat, twice)) { - log_debug(ld->log, "Attept to pay %s with amount %s > %s", - type_to_string(tmpctx, struct sha256, - &details->rhash), - type_to_string(tmpctx, struct amount_msat, &msat), - type_to_string(tmpctx, struct amount_msat, &twice)); + *err = tal_fmt(ctx, "Attempt to pay %s with amount %s > %s", + type_to_string(tmpctx, struct sha256, + &details->rhash), + type_to_string(tmpctx, struct amount_msat, &msat), + type_to_string(tmpctx, struct amount_msat, &twice)); /* BOLT #4: * * - if the amount paid is more than twice the amount diff --git a/lightningd/invoice.h b/lightningd/invoice.h index 012bb832a37c..5579d87b5b1f 100644 --- a/lightningd/invoice.h +++ b/lightningd/invoice.h @@ -47,6 +47,7 @@ struct invoice_details { * @payment_hash: hash of preimage they want. * @msat: amount they offer to pay. * @payment_secret: they payment secret they sent, if any. + * @err: error string if it returns NULL. * * Returns NULL if there's a problem, otherwise returns the invoice details. */ @@ -55,7 +56,8 @@ invoice_check_payment(const tal_t *ctx, struct lightningd *ld, const struct sha256 *payment_hash, const struct amount_msat msat, - const struct secret *payment_secret); + const struct secret *payment_secret, + const char **err); /** * invoice_try_pay - process payment for these incoming payments. diff --git a/lightningd/pay.c b/lightningd/pay.c index 62d24963ca80..88053581449e 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -11,9 +12,11 @@ #include #include #include +#include #include #include #include +#include /* Routing failure object */ struct routing_failure { @@ -326,23 +329,25 @@ static void tell_waiters_success(struct lightningd *ld, notify_sendpay_success(ld, payment); } -void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, +void payment_succeeded(struct lightningd *ld, + const struct sha256 *payment_hash, + u64 partid, u64 groupid, const struct preimage *rval) { struct wallet_payment *payment; - wallet_payment_set_status(ld->wallet, &hout->payment_hash, - hout->partid, hout->groupid, + wallet_payment_set_status(ld->wallet, payment_hash, + partid, groupid, PAYMENT_COMPLETE, rval); payment = wallet_payment_by_hash(tmpctx, ld->wallet, - &hout->payment_hash, - hout->partid, hout->groupid); + payment_hash, + partid, groupid); assert(payment); if (payment->local_invreq_id) wallet_invoice_request_mark_used(ld->wallet->db, payment->local_invreq_id); - tell_waiters_success(ld, &hout->payment_hash, payment); + tell_waiters_success(ld, payment_hash, payment); } /* Return a struct routing_failure for an immediate failure @@ -858,35 +863,24 @@ static struct channel *find_channel_for_htlc_add(struct lightningd *ld, return NULL; } -/* destination/route_channels/route_nodes are NULL (and path_secrets may be NULL) - * if we're sending a raw onion. */ -static struct command_result * -send_payment_core(struct lightningd *ld, - struct command *cmd, - const struct sha256 *rhash, - u64 partid, - u64 group, - const struct route_hop *first_hop, - struct amount_msat msat, - 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, - struct short_channel_id *route_channels TAKES, - struct secret *path_secrets, - const struct sha256 *local_invreq_id) +/* Check if payment already in progress. Returns NULL if all good; + * sets old_payment to a previous attempt if there is one (otherwise + * NULL). */ +static struct command_result *check_progress(struct lightningd *ld, + struct command *cmd, + const struct sha256 *rhash, + struct amount_msat msat, + struct amount_msat total_msat, + u64 partid, + u64 group, + const struct node_id *destination, + const struct wallet_payment **old_payment) { - const struct wallet_payment **payments, *old_payment = NULL; - struct channel *channel; - const u8 *failmsg; - struct htlc_out *hout; - struct routing_failure *fail; - struct amount_msat msat_already_pending = AMOUNT_MSAT(0); + const struct wallet_payment **payments; bool have_complete = false; - struct command_result *invreq_err; + struct amount_msat msat_already_pending = AMOUNT_MSAT(0); + + *old_payment = NULL; /* Now, do we already have one or more payments? */ payments = wallet_payment_list(tmpctx, ld->wallet, rhash); @@ -1001,7 +995,7 @@ send_payment_core(struct lightningd *ld, case PAYMENT_FAILED: if (payments[i]->partid == partid) - old_payment = payments[i]; + *old_payment = payments[i]; } /* There is no way for us to add a payment with the * same (payment_hash, partid, groupid) tuple since @@ -1018,7 +1012,6 @@ send_payment_core(struct lightningd *ld, type_to_string(tmpctx, struct sha256, rhash), group, partid); } - } /* If any part has succeeded, you can't start a new one! */ @@ -1043,9 +1036,47 @@ send_payment_core(struct lightningd *ld, &total_msat)); } - invreq_err = check_invoice_request_usage(cmd, local_invreq_id); - if (invreq_err) - return invreq_err; + return NULL; +} + +/* destination/route_channels/route_nodes are NULL (and path_secrets may be NULL) + * if we're sending a raw onion. */ +static struct command_result * +send_payment_core(struct lightningd *ld, + struct command *cmd, + const struct sha256 *rhash, + u64 partid, + u64 group, + const struct route_hop *first_hop, + struct amount_msat msat, + 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, + struct short_channel_id *route_channels TAKES, + struct secret *path_secrets, + const struct sha256 *local_invreq_id) +{ + const struct wallet_payment *old_payment; + struct channel *channel; + const u8 *failmsg; + struct htlc_out *hout; + struct routing_failure *fail; + struct command_result *ret; + struct wallet_payment *payment; + + /* Reconcile this with previous attempts */ + ret = check_progress(ld, cmd, rhash, msat, total_msat, partid, group, destination, + &old_payment); + if (ret) + return ret; + + ret = check_invoice_request_usage(cmd, local_invreq_id); + if (ret) + return ret; channel = find_channel_for_htlc_add(ld, &first_hop->node_id, &first_hop->scid); @@ -1091,39 +1122,27 @@ send_payment_core(struct lightningd *ld, } /* If hout fails, payment should be freed too. */ - struct wallet_payment *payment = tal(hout, struct wallet_payment); - payment->id = 0; - payment->payment_hash = *rhash; - payment->partid = partid; - payment->groupid = group; - payment->destination = tal_dup_or_null(payment, struct node_id, - destination); - payment->status = PAYMENT_PENDING; - payment->msatoshi = msat; - payment->msatoshi_sent = first_hop->amount; - payment->total_msat = total_msat; - payment->timestamp = time_now().ts.tv_sec; - payment->payment_preimage = NULL; - payment->path_secrets = tal_steal(payment, path_secrets); - payment->route_nodes = tal_steal(payment, route_nodes); - payment->route_channels = tal_steal(payment, route_channels); - payment->failonion = NULL; - payment->completed_at = NULL; - - if (label != NULL) - payment->label = tal_strdup(payment, label); - else - payment->label = NULL; - if (invstring != NULL) - 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_invreq_id = tal_dup_or_null(payment, struct sha256, - local_invreq_id); + payment = wallet_payment_new(hout, + 0, /* ID is not in db yet */ + time_now().ts.tv_sec, + NULL, + rhash, + partid, + group, + PAYMENT_PENDING, + destination, + msat, + first_hop->amount, + total_msat, + NULL, + path_secrets, + route_nodes, + route_channels, + invstring, + label, + description, + NULL, + local_invreq_id); /* We write this into db when HTLC is actually sent. */ wallet_payment_setup(ld->wallet, payment); @@ -1361,9 +1380,9 @@ static struct command_result *param_route_hops(struct command *cmd, size_t i; const jsmntok_t *t; - if (tok->type != JSMN_ARRAY || tok->size == 0) + if (tok->type != JSMN_ARRAY) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s must be an (non-empty) array", name); + "%s must be an array", name); *hops = tal_arr(cmd, struct route_hop, tok->size); json_for_each_arr(i, t, tok) { @@ -1394,6 +1413,106 @@ static struct command_result *param_route_hops(struct command *cmd, return NULL; } +/* We're paying ourselves! */ +static struct command_result *self_payment(struct lightningd *ld, + struct command *cmd, + const struct sha256 *rhash, + u64 partid, + u64 groupid, + struct amount_msat msat, + const char *label TAKES, + const char *invstring TAKES, + const char *description TAKES, + const struct sha256 *local_invreq_id, + const struct secret *payment_secret, + const u8 *payment_metadata) +{ + struct wallet_payment *payment; + const struct invoice_details *inv; + u64 inv_dbid; + const char *err; + + payment = wallet_payment_new(tmpctx, + 0, /* ID is not in db yet */ + time_now().ts.tv_sec, + NULL, + rhash, + partid, + groupid, + PAYMENT_PENDING, + &ld->id, + msat, + msat, + msat, + NULL, + NULL, + NULL, + NULL, + invstring, + label, + description, + NULL, + local_invreq_id); + + /* We write this into db immediately, but we're expected to do + * it in two stages like a normal payment. */ + wallet_payment_setup(ld->wallet, payment); + payment_store(ld, payment); + + /* Now, resolved the invoice */ + inv = invoice_check_payment(tmpctx, ld, rhash, msat, payment_secret, &err); + if (!inv) { + struct routing_failure *fail; + wallet_payment_set_status(ld->wallet, rhash, partid, groupid, + PAYMENT_FAILED, NULL); + + /* tell_waiters_failed expects one of these! */ + fail = tal(payment, struct routing_failure); + fail->failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS; + fail->erring_node = &ld->id; + fail->erring_index = 0; + fail->erring_channel = NULL; + fail->msg = NULL; + + /* Only some of these fields make sense for self payments */ + wallet_payment_set_failinfo(ld->wallet, + rhash, + partid, NULL, + true, + 0, + fail->failcode, fail->erring_node, + NULL, NULL, + err, + 0); + /* We do this even though there really can't be any waiters, + * since we didn't block. */ + tell_waiters_failed(ld, rhash, payment, PAY_DESTINATION_PERM_FAIL, + NULL, fail, err); + return sendpay_fail(cmd, payment, PAY_DESTINATION_PERM_FAIL, NULL, + fail, err); + } + + /* These should not fail, given the above succeded! */ + if (!invoices_find_by_rhash(ld->wallet->invoices, &inv_dbid, rhash) + || !invoices_resolve(ld->wallet->invoices, inv_dbid, msat)) { + log_broken(ld->log, "Could not resolve invoice %"PRIu64"!?!", inv_dbid); + return sendpay_fail(cmd, payment, PAY_DESTINATION_PERM_FAIL, NULL, NULL, "broken"); + } + + log_info(ld->log, "Self-resolved invoice '%s' with amount %s", + inv->label->s, + type_to_string(tmpctx, struct amount_msat, &msat)); + notify_invoice_payment(ld, msat, inv->r, inv->label); + + /* Now resolve the payment */ + payment_succeeded(ld, rhash, partid, groupid, &inv->r); + + /* Now the specific command which called this. */ + payment->status = PAYMENT_COMPLETE; + payment->payment_preimage = tal_dup(payment, struct preimage, &inv->r); + return sendpay_success(cmd, payment); +} + static struct command_result *json_sendpay(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -1429,14 +1548,26 @@ static struct command_result *json_sendpay(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Must specify msatoshi with partid"); - const struct amount_msat final_amount = route[tal_count(route)-1].amount; - /* If groupid was not provided default to incrementing from the previous one. */ if (group == NULL) { group = tal(tmpctx, u64); *group = wallet_payment_get_groupid(cmd->ld->wallet, rhash) + 1; } + if (tal_count(route) == 0) { + if (!msat) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Self-payment requires amount_msat"); + if (*partid) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Self-payment does not allow (non-zero) partid"); + return self_payment(cmd->ld, cmd, rhash, *partid, *group, *msat, + label, invstring, description, local_invreq_id, + payment_secret, payment_metadata); + } + + const struct amount_msat final_amount = route[tal_count(route)-1].amount; + if (msat && !*partid && !amount_msat_eq(*msat, final_amount)) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Do not specify msatoshi (%s) without" diff --git a/lightningd/pay.h b/lightningd/pay.h index 439a2bd7e448..d8444f4f97cc 100644 --- a/lightningd/pay.h +++ b/lightningd/pay.h @@ -12,7 +12,9 @@ struct json_stream; struct wallet_payment; struct routing_failure; -void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, +void payment_succeeded(struct lightningd *ld, + const struct sha256 *payment_hash, + u64 partid, u64 groupid, const struct preimage *rval); /* hout->failmsg or hout->failonion must be set. */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index cc7f8582f679..69f9fe2d2eb7 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1462,7 +1462,7 @@ static void fulfill_our_htlc_out(struct channel *channel, struct htlc_out *hout, hout->msat); if (hout->am_origin) - payment_succeeded(ld, hout, preimage); + payment_succeeded(ld, &hout->payment_hash, hout->partid, hout->groupid, preimage); else if (hout->in) { fulfill_htlc(hout->in, preimage); wallet_forwarded_payment_add(ld->wallet, hout->in, diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index 2456aec48062..fe392f486e84 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -262,10 +262,7 @@ bool plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, ph_req->cb_arg = tal_steal(ph_req, cb_arg); ph_req->db = ld->wallet->db; ph_req->ld = ld; - if (cmd_id) - ph_req->cmd_id = tal_strdup(ph_req, cmd_id); - else - ph_req->cmd_id = NULL; + ph_req->cmd_id = tal_strdup_or_null(ph_req, cmd_id); list_head_init(&ph_req->call_chain); for (size_t i=0; ihooks); i++) { diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 51dda2456c6c..ee357997e0f7 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -47,12 +47,7 @@ static struct income_event *chain_to_income(const tal_t *ctx, inc->currency = tal_strdup(inc, ev->currency); inc->timestamp = ev->timestamp; inc->outpoint = tal_dup(inc, struct bitcoin_outpoint, &ev->outpoint); - - if (ev->desc) - inc->desc = tal_strdup(inc, ev->desc); - else - inc->desc = NULL; - + inc->desc = tal_strdup_or_null(inc, ev->desc); inc->txid = tal_dup_or_null(inc, struct bitcoin_txid, ev->spending_txid); inc->payment_id = tal_dup_or_null(inc, struct sha256, ev->payment_id); @@ -75,10 +70,7 @@ static struct income_event *channel_to_income(const tal_t *ctx, inc->timestamp = ev->timestamp; inc->outpoint = NULL; inc->txid = NULL; - if (ev->desc) - inc->desc = tal_strdup(inc, ev->desc); - else - inc->desc = NULL; + inc->desc = tal_strdup_or_null(inc, ev->desc); inc->payment_id = tal_dup_or_null(inc, struct sha256, ev->payment_id); return inc; diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 8f797fb9e8d7..bc72093b904b 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -32,9 +32,9 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st e->tag = db_col_strdup(e, stmt, "e.tag"); - db_col_amount_msat(stmt, "e.credit", &e->credit); - db_col_amount_msat(stmt, "e.debit", &e->debit); - db_col_amount_msat(stmt, "e.output_value", &e->output_value); + e->credit = db_col_amount_msat(stmt, "e.credit"); + e->debit = db_col_amount_msat(stmt, "e.debit"); + e->output_value = db_col_amount_msat(stmt, "e.output_value"); e->currency = db_col_strdup(e, stmt, "e.currency"); e->timestamp = db_col_u64(stmt, "e.timestamp"); @@ -96,9 +96,9 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt e->tag = db_col_strdup(e, stmt, "e.tag"); - db_col_amount_msat(stmt, "e.credit", &e->credit); - db_col_amount_msat(stmt, "e.debit", &e->debit); - db_col_amount_msat(stmt, "e.fees", &e->fees); + e->credit = db_col_amount_msat(stmt, "e.credit"); + e->debit = db_col_amount_msat(stmt, "e.debit"); + e->fees = db_col_amount_msat(stmt, "e.fees"); e->currency = db_col_strdup(e, stmt, "e.currency"); if (!db_col_is_null(stmt, "e.payment_id")) { @@ -131,8 +131,8 @@ static struct rebalance *stmt2rebalance(const tal_t *ctx, struct db_stmt *stmt) r->out_ev_id = db_col_u64(stmt, "out_e.id"); r->in_acct_name = db_col_strdup(r, stmt, "in_acct.name"); r->out_acct_name = db_col_strdup(r, stmt, "out_acct.name"); - db_col_amount_msat(stmt, "in_e.credit", &r->rebal_msat); - db_col_amount_msat(stmt, "out_e.fees", &r->fee_msat); + r->rebal_msat = db_col_amount_msat(stmt, "in_e.credit"); + r->fee_msat = db_col_amount_msat(stmt, "out_e.fees"); return r; } @@ -289,8 +289,8 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) sum->acct_db_id = db_col_u64(stmt, "of.account_id"); sum->acct_name = db_col_strdup(sum, stmt, "a.name"); sum->currency = db_col_strdup(sum, stmt, "of.currency"); - db_col_amount_msat(stmt, "credit", &sum->fees_paid); - db_col_amount_msat(stmt, "debit", &debit); + sum->fees_paid = db_col_amount_msat(stmt, "credit"); + debit = db_col_amount_msat(stmt, "debit"); ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, debit); @@ -359,8 +359,8 @@ struct fee_sum **find_account_onchain_fees(const tal_t *ctx, sum->txid = tal(sum, struct bitcoin_txid); db_col_txid(stmt, "txid", sum->txid); - db_col_amount_msat(stmt, "credit", &sum->fees_paid); - db_col_amount_msat(stmt, "debit", &amt); + sum->fees_paid = db_col_amount_msat(stmt, "credit"); + amt = db_col_amount_msat(stmt, "debit"); ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, amt); assert(ok); tal_arr_expand(&sums, sum); @@ -818,8 +818,8 @@ char *account_get_balance(const tal_t *ctx, bal = tal(*balances, struct acct_balance); bal->currency = db_col_strdup(bal, stmt, "ce.currency"); - db_col_amount_msat(stmt, "credit", &bal->credit); - db_col_amount_msat(stmt, "debit", &bal->debit); + bal->credit = db_col_amount_msat(stmt, "credit"); + bal->debit = db_col_amount_msat(stmt, "debit"); tal_arr_expand(balances, bal); if (account_exists) @@ -862,13 +862,13 @@ char *account_get_balance(const tal_t *ctx, tal_arr_expand(balances, bal); } - db_col_amount_msat(stmt, "credit", &amt); + amt = db_col_amount_msat(stmt, "credit"); if (!amount_msat_add(&bal->credit, bal->credit, amt)) { tal_free(stmt); return "overflow adding channel_event credits"; } - db_col_amount_msat(stmt, "debit", &amt); + amt = db_col_amount_msat(stmt, "debit"); if (!amount_msat_add(&bal->debit, bal->debit, amt)) { tal_free(stmt); return "overflow adding channel_event debits"; @@ -992,8 +992,8 @@ static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, of->acct_db_id = db_col_u64(stmt, "of.account_id"); of->acct_name = db_col_strdup(of, stmt, "a.name"); db_col_txid(stmt, "of.txid", &of->txid); - db_col_amount_msat(stmt, "of.credit", &of->credit); - db_col_amount_msat(stmt, "of.debit", &of->debit); + of->credit = db_col_amount_msat(stmt, "of.credit"); + of->debit = db_col_amount_msat(stmt, "of.debit"); of->currency = db_col_strdup(of, stmt, "of.currency"); of->timestamp = db_col_u64(stmt, "of.timestamp"); of->update_count = db_col_int(stmt, "of.update_count"); @@ -1490,8 +1490,8 @@ static void insert_chain_fees_diff(struct db *db, update_count = 0; while (db_step(stmt)) { update_count = db_col_int(stmt, "update_count"); - db_col_amount_msat(stmt, "credit", &credit); - db_col_amount_msat(stmt, "debit", &debit); + credit = db_col_amount_msat(stmt, "credit"); + debit = db_col_amount_msat(stmt, "debit"); /* These should apply perfectly, as we sorted them by * insert order */ diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index eb46dbabfd6f..9885af6c4877 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1933,6 +1933,45 @@ static void payment_notify_failure(struct payment *p, const char *error_message) plugin_notification_end(p->plugin, n); } +/* Code shared by selfpay fast-path: populate JSON output for successful + * payment, and send pay_success notification. */ +void json_add_payment_success(struct json_stream *js, + struct payment *p, + const struct preimage *preimage, + const struct payment_tree_result *result) +{ + struct json_stream *n; + struct payment *root = payment_root(p); + + json_add_node_id(js, "destination", p->destination); + json_add_sha256(js, "payment_hash", p->payment_hash); + json_add_timeabs(js, "created_at", p->start_time); + if (result) + json_add_num(js, "parts", result->attempts); + else + json_add_num(js, "parts", 1); + + json_add_amount_msat(js, "amount_msat", p->amount); + if (result) + json_add_amount_msat(js, "amount_sent_msat", result->sent); + else + json_add_amount_msat(js, "amount_sent_msat", p->amount); + + if (result && result->leafstates != PAYMENT_STEP_SUCCESS) + json_add_string(js, "warning_partial_completion", + "Some parts of the payment are not yet " + "completed, but we have the confirmation " + "from the recipient."); + json_add_preimage(js, "payment_preimage", preimage); + json_add_string(js, "status", "complete"); + + n = plugin_notification_start(p->plugin, "pay_success"); + json_add_sha256(n, "payment_hash", p->payment_hash); + if (root->invstring != NULL) + json_add_string(n, "bolt11", root->invstring); + plugin_notification_end(p->plugin, n); +} + /* This function is called whenever a payment ends up in a final state, or all * leafs in the subtree rooted in the payment are all in a final state. It is * called only once, and it is guaranteed to be called in post-order @@ -1943,8 +1982,6 @@ static void payment_finished(struct payment *p) struct json_stream *ret; struct command *cmd = p->cmd; const char *msg; - struct json_stream *n; - struct payment *root = payment_root(p); /* Either none of the leaf attempts succeeded yet, or we have a * preimage. */ @@ -1969,30 +2006,8 @@ static void payment_finished(struct payment *p) p->on_payment_success(p); ret = jsonrpc_stream_success(cmd); - json_add_node_id(ret, "destination", p->destination); - json_add_sha256(ret, "payment_hash", p->payment_hash); - json_add_timeabs(ret, "created_at", p->start_time); - json_add_num(ret, "parts", result.attempts); - - json_add_amount_msat(ret, "amount_msat", p->amount); - json_add_amount_msat(ret, "amount_sent_msat", - result.sent); - - if (result.leafstates != PAYMENT_STEP_SUCCESS) - json_add_string( - ret, "warning_partial_completion", - "Some parts of the payment are not yet " - "completed, but we have the confirmation " - "from the recipient."); - json_add_preimage(ret, "payment_preimage", result.preimage); - - json_add_string(ret, "status", "complete"); - - n = plugin_notification_start(p->plugin, "pay_success"); - json_add_sha256(n, "payment_hash", p->payment_hash); - if (root->invstring != NULL) - json_add_string(n, "bolt11", root->invstring); - plugin_notification_end(p->plugin, n); + json_add_payment_success(ret, p, result.preimage, + &result); if (command_finished(cmd, ret)) {/* Ignore result. */} p->cmd = NULL; diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 7d019f331cf3..06abcde633a3 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -484,6 +484,12 @@ void payment_abort(struct payment *p, const char *fmt, ...) PRINTF_FMT(2,3); struct payment *payment_root(struct payment *p); struct payment_tree_result payment_collect_result(struct payment *p); +/* Add fields for successful payment: result can be NULL for selfpay */ +void json_add_payment_success(struct json_stream *js, + struct payment *p, + const struct preimage *preimage, + const struct payment_tree_result *result); + /* For special effects, like inspecting your own routes. */ struct gossmap *get_gossmap(struct plugin *plugin); diff --git a/plugins/pay.c b/plugins/pay.c index ac3fe0de9776..0799a2c82052 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -799,6 +799,55 @@ static void on_payment_failure(struct payment *payment) } } +static struct command_result *selfpay_success(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct payment *p) +{ + struct json_stream *ret = jsonrpc_stream_success(cmd); + struct preimage preimage; + const char *err; + + err = json_scan(tmpctx, buf, result, + "{payment_preimage:%}", + JSON_SCAN(json_to_preimage, &preimage)); + if (err) + plugin_err(p->plugin, + "selfpay didn't have payment_preimage? %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + json_add_payment_success(ret, p, &preimage, NULL); + return command_finished(cmd, ret); +} + +static struct command_result *selfpay(struct command *cmd, struct payment *p) +{ + struct out_req *req; + + /* This "struct payment" simply gets freed once command is done. */ + tal_steal(cmd, p); + + req = jsonrpc_request_start(cmd->plugin, cmd, "sendpay", + selfpay_success, + forward_error, p); + /* Empty route means "to-self" */ + json_array_start(req->js, "route"); + json_array_end(req->js); + json_add_sha256(req->js, "payment_hash", p->payment_hash); + if (p->label) + json_add_string(req->js, "label", p->label); + json_add_amount_msat(req->js, "amount_msat", p->amount); + json_add_string(req->js, "bolt11", p->invstring); + if (p->payment_secret) + json_add_secret(req->js, "payment_secret", p->payment_secret); + json_add_u64(req->js, "groupid", p->groupid); + if (p->payment_metadata) + json_add_hex_talarr(req->js, "payment_metadata", p->payment_metadata); + if (p->description) + json_add_string(req->js, "description", p->description); + return send_outreq(cmd->plugin, req); +} + /* We are interested in any prior attempts to pay this payment_hash / * invoice so we can set the `groupid` correctly and ensure we don't * already have a pending payment running. We also collect the summary @@ -916,6 +965,11 @@ payment_listsendpays_previous(struct command *cmd, const char *buf, p->groupid = last_group + 1; p->on_payment_success = on_payment_success; p->on_payment_failure = on_payment_failure; + + /* Bypass everything if we're doing (synchronous) self-pay */ + if (node_id_eq(&my_id, p->destination)) + return selfpay(cmd, p); + payment_start(p); return command_still_pending(cmd); } @@ -1166,13 +1220,8 @@ static struct command_result *json_pay(struct command *cmd, "This payment blinded path fee overflows!"); } - if (node_id_eq(&my_id, p->destination)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "This payment is destined for ourselves. " - "Self-payments are not supported"); - p->local_id = &my_id; - p->json_buffer = tal_steal(p, buf); + p->json_buffer = buf; p->json_toks = params; p->why = "Initial attempt"; p->constraints.cltv_budget = *maxdelay; diff --git a/tests/test_pay.py b/tests/test_pay.py index 406938e40a74..4122da3e2cf3 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4830,9 +4830,17 @@ def test_self_pay(node_factory): l1, l2 = node_factory.line_graph(2, wait_for_announce=True) inv = l1.rpc.invoice(10000, 'test', 'test')['bolt11'] + l1.rpc.pay(inv) - with pytest.raises(RpcError): - l1.rpc.pay(inv) + # We can pay twice, no problem! + l1.rpc.pay(inv) + + inv2 = l1.rpc.invoice(10000, 'test2', 'test2')['bolt11'] + l1.rpc.delinvoice('test2', 'unpaid') + + with pytest.raises(RpcError, match=r'Unknown invoice') as excinfo: + l1.rpc.pay(inv2) + assert excinfo.value.error['code'] == 203 @unittest.skipIf(TEST_NETWORK != 'regtest', "Canned invoice is network specific") @@ -5289,6 +5297,51 @@ def test_invoice_pay_desc_with_quotes(node_factory): l1.rpc.pay(invoice, description=description) +def test_self_sendpay(node_factory): + """We get much more descriptive errors from a self-payment than a remote payment, since we're not relying on a single WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS but can share more useful information""" + l1 = node_factory.get_node() + + inv = l1.rpc.invoice('100000sat', 'test_selfpay', "Test of payment to self") + assert only_one(l1.rpc.listinvoices()['invoices'])['status'] == 'unpaid' + inv_expires = l1.rpc.invoice('1btc', 'test_selfpay-expires', "Test of payment to self", expiry=1) + + # Requires amount. + with pytest.raises(RpcError, match="Self-payment requires amount_msat"): + l1.rpc.sendpay([], inv['payment_hash'], label='selfpay', bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) + + # Requires non-zero partid. + with pytest.raises(RpcError, match=r"Self-payment does not allow \(non-zero\) partid"): + l1.rpc.sendpay([], inv['payment_hash'], label='selfpay', bolt11=inv['bolt11'], payment_secret=inv['payment_secret'], amount_msat='100000sat', partid=1) + + # Bad payment_hash. + with pytest.raises(RpcError, match="Unknown invoice"): + l1.rpc.sendpay([], '00' * 32, label='selfpay-badimage', bolt11=inv['bolt11'], payment_secret=inv['payment_secret'], amount_msat='100000sat') + + # Missing payment_secret + with pytest.raises(RpcError, match="Attempt to pay .* without secret"): + l1.rpc.sendpay([], inv['payment_hash'], label='selfpay-badimage', bolt11=inv['bolt11'], amount_msat='100000sat') + + # Bad payment_secret + with pytest.raises(RpcError, match="Attempt to pay .* with wrong secret"): + l1.rpc.sendpay([], inv['payment_hash'], label='selfpay-badimage', bolt11=inv['bolt11'], payment_secret='00' * 32, amount_msat='100000sat') + + # Expired + time.sleep(2) + with pytest.raises(RpcError, match="Already paid or expired invoice"): + l1.rpc.sendpay([], inv_expires['payment_hash'], label='selfpay-badimage', bolt11=inv_expires['bolt11'], payment_secret=inv['payment_secret'], amount_msat='1btc') + + # This one works! + l1.rpc.sendpay([], inv['payment_hash'], label='selfpay', bolt11=inv['bolt11'], payment_secret=inv['payment_secret'], amount_msat='100000sat') + + assert only_one(l1.rpc.listinvoices(payment_hash=inv['payment_hash'])['invoices'])['status'] == 'paid' + # Only one is complete. + assert [p['status'] for p in l1.rpc.listsendpays()['payments'] if p['status'] != 'failed'] == ['complete'] + + # Can't pay paid one already paid! + with pytest.raises(RpcError, match="Already paid or expired invoice"): + l1.rpc.sendpay([], inv['payment_hash'], label='selfpay', bolt11=inv['bolt11'], payment_secret=inv['payment_secret'], amount_msat='100000sat') + + def test_strip_lightning_suffix_from_inv(node_factory): """ Reproducer for [1] that pay an invoice with the `lightning:` diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 987f53c00e1e..01c0a7bd642e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1917,7 +1917,7 @@ def test_replacement_payload(node_factory): with pytest.raises(RpcError, match=r"WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS \(reply from remote\)"): l1.rpc.pay(inv) - assert l2.daemon.wait_for_log("Attept to pay.*with wrong secret") + assert l2.daemon.wait_for_log("Attempt to pay.*with wrong secret") @pytest.mark.developer("Requires dev_sign_last_tx") diff --git a/wallet/db.c b/wallet/db.c index 45fefef7ccee..b5f626ebf8aa 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1350,7 +1350,7 @@ migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db) continue; } db_col_node_id(stmt, "p.node_id", &peer_id); - db_col_amount_sat(stmt, "inflight.funding_satoshi", &funding_sat); + funding_sat = db_col_amount_sat(stmt, "inflight.funding_satoshi"); db_col_pubkey(stmt, "c.fundingkey_remote", &remote_funding_pubkey); db_col_txid(stmt, "inflight.funding_tx_id", &funding_txid); @@ -1439,7 +1439,7 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db) } db_col_node_id(stmt, "p.node_id", &peer_id); - db_col_amount_sat(stmt, "c.funding_satoshi", &funding_sat); + funding_sat = db_col_amount_sat(stmt, "c.funding_satoshi"); db_col_pubkey(stmt, "c.fundingkey_remote", &remote_funding_pubkey); get_channel_basepoints(ld, &peer_id, cdb_id, diff --git a/wallet/invoices.c b/wallet/invoices.c index 4badea7d1a19..445550a9c35a 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -88,12 +88,17 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, dtl->label = db_col_json_escape(dtl, stmt, "label"); - dtl->msat = db_col_optional(dtl, stmt, "msatoshi", amount_msat); + if (db_col_is_null(stmt, "msatoshi")) + dtl->msat = NULL; + else { + dtl->msat = tal(dtl, struct amount_msat); + *dtl->msat = db_col_amount_msat(stmt, "msatoshi"); + } dtl->expiry_time = db_col_u64(stmt, "expiry_time"); if (dtl->state == PAID) { dtl->pay_index = db_col_u64(stmt, "pay_index"); - db_col_amount_msat(stmt, "msatoshi_received", &dtl->received); + dtl->received = db_col_amount_msat(stmt, "msatoshi_received"); dtl->paid_timestamp = db_col_u64(stmt, "paid_timestamp"); } else { db_col_ignore(stmt, "pay_index"); @@ -102,13 +107,7 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, } dtl->invstring = db_col_strdup(dtl, stmt, "bolt11"); - - if (!db_col_is_null(stmt, "description")) - dtl->description = db_col_strdup(dtl, stmt, - "description"); - else - dtl->description = NULL; - + dtl->description = db_col_strdup_optional(dtl, stmt, "description"); dtl->features = db_col_arr(dtl, stmt, "features", u8); dtl->local_offer_id = db_col_optional(dtl, stmt, "local_offer_id", sha256); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 9caeb3ba8a9e..d8b350c4f5d4 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -621,7 +621,9 @@ void payment_failed(struct lightningd *ld UNNEEDED, const struct htlc_out *hout void payment_store(struct lightningd *ld UNNEEDED, struct wallet_payment *payment UNNEEDED) { fprintf(stderr, "payment_store called!\n"); abort(); } /* Generated stub for payment_succeeded */ -void payment_succeeded(struct lightningd *ld UNNEEDED, struct htlc_out *hout UNNEEDED, +void payment_succeeded(struct lightningd *ld UNNEEDED, + const struct sha256 *payment_hash UNNEEDED, + u64 partid UNNEEDED, u64 groupid UNNEEDED, const struct preimage *rval UNNEEDED) { fprintf(stderr, "payment_succeeded called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ diff --git a/wallet/wallet.c b/wallet/wallet.c index 28155e6fd025..d80733506428 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -209,7 +209,7 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt) u32 *blockheight, *spendheight; db_col_txid(stmt, "prev_out_tx", &utxo->outpoint.txid); utxo->outpoint.n = db_col_int(stmt, "prev_out_index"); - db_col_amount_sat(stmt, "value", &utxo->amount); + utxo->amount = db_col_amount_sat(stmt, "value"); utxo->is_p2sh = db_col_int(stmt, "type") == p2sh_wpkh; utxo->status = db_col_int(stmt, "status"); utxo->keyindex = db_col_int(stmt, "keyindex"); @@ -1233,8 +1233,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, db_col_txid(stmt, "funding_tx_id", &funding.txid); funding.n = db_col_int(stmt, "funding_tx_outnum"), - db_col_amount_sat(stmt, "funding_satoshi", &funding_sat); - db_col_amount_sat(stmt, "our_funding_satoshi", &our_funding_sat); + funding_sat = db_col_amount_sat(stmt, "funding_satoshi"); + our_funding_sat = db_col_amount_sat(stmt, "our_funding_satoshi"); if (!db_col_signature(stmt, "last_sig", &last_sig.s)) return NULL; @@ -1246,8 +1246,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, lease_chan_max_msat = db_col_u64(stmt, "lease_chan_max_msat"); lease_chan_max_ppt = db_col_int(stmt, "lease_chan_max_ppt"); lease_blockheight_start = db_col_int(stmt, "lease_blockheight_start"); - db_col_amount_msat(stmt, "lease_fee", &lease_fee); - db_col_amount_sat(stmt, "lease_satoshi", &lease_amt); + lease_fee = db_col_amount_msat(stmt, "lease_fee"); + lease_amt = db_col_amount_sat(stmt, "lease_satoshi"); } else { lease_commit_sig = NULL; lease_chan_max_msat = 0; @@ -1354,16 +1354,13 @@ static bool wallet_channel_config_load(struct wallet *w, const u64 id, return false; cc->id = id; - db_col_amount_sat(stmt, "dust_limit_satoshis", &cc->dust_limit); - db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat", - &cc->max_htlc_value_in_flight); - db_col_amount_sat(stmt, "channel_reserve_satoshis", - &cc->channel_reserve); - db_col_amount_msat(stmt, "htlc_minimum_msat", &cc->htlc_minimum); + cc->dust_limit = db_col_amount_sat(stmt, "dust_limit_satoshis"); + cc->max_htlc_value_in_flight = db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat"); + cc->channel_reserve = db_col_amount_sat(stmt, "channel_reserve_satoshis"); + cc->htlc_minimum = db_col_amount_msat(stmt, "htlc_minimum_msat"); cc->to_self_delay = db_col_int(stmt, "to_self_delay"); cc->max_accepted_htlcs = db_col_int(stmt, "max_accepted_htlcs"); - db_col_amount_msat(stmt, "max_dust_htlc_exposure_msat", - &cc->max_dust_htlc_exposure_msat); + cc->max_dust_htlc_exposure_msat = db_col_amount_msat(stmt, "max_dust_htlc_exposure_msat"); tal_free(stmt); return ok; } @@ -1526,14 +1523,14 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm = db_col_int(stmt, "shutdown_wrong_outnum"); } - db_col_amount_sat(stmt, "funding_satoshi", &funding_sat); - db_col_amount_sat(stmt, "our_funding_satoshi", &our_funding_sat); - db_col_amount_msat(stmt, "push_msatoshi", &push_msat); - db_col_amount_msat(stmt, "msatoshi_local", &our_msat); - db_col_amount_msat(stmt, "msatoshi_to_us_min", &msat_to_us_min); - db_col_amount_msat(stmt, "msatoshi_to_us_max", &msat_to_us_max); - db_col_amount_msat(stmt, "htlc_minimum_msat", &htlc_minimum_msat); - db_col_amount_msat(stmt, "htlc_maximum_msat", &htlc_maximum_msat); + funding_sat = db_col_amount_sat(stmt, "funding_satoshi"); + our_funding_sat = db_col_amount_sat(stmt, "our_funding_satoshi"); + push_msat = db_col_amount_msat(stmt, "push_msatoshi"); + our_msat = db_col_amount_msat(stmt, "msatoshi_local"); + msat_to_us_min = db_col_amount_msat(stmt, "msatoshi_to_us_min"); + msat_to_us_max = db_col_amount_msat(stmt, "msatoshi_to_us_max"); + htlc_minimum_msat = db_col_amount_msat(stmt, "htlc_minimum_msat"); + htlc_maximum_msat = db_col_amount_msat(stmt, "htlc_maximum_msat"); ignore_fee_limits = db_col_int(stmt, "ignore_fee_limits"); if (!db_col_is_null(stmt, "lease_commit_sig")) { @@ -1654,11 +1651,11 @@ static struct closed_channel *wallet_stmt2closed_channel(const tal_t *ctx, cc->next_htlc_id = db_col_u64(stmt, "next_htlc_id"); db_col_sha256d(stmt, "funding_tx_id", &cc->funding.txid.shad); cc->funding.n = db_col_int(stmt, "funding_tx_outnum"); - db_col_amount_sat(stmt, "funding_satoshi", &cc->funding_sats); - db_col_amount_msat(stmt, "push_msatoshi", &cc->push); - db_col_amount_msat(stmt, "msatoshi_local", &cc->our_msat); - db_col_amount_msat(stmt, "msatoshi_to_us_min", &cc->msat_to_us_min); - db_col_amount_msat(stmt, "msatoshi_to_us_max", &cc->msat_to_us_max); + cc->funding_sats = db_col_amount_sat(stmt, "funding_satoshi"); + cc->push = db_col_amount_msat(stmt, "push_msatoshi"); + cc->our_msat = db_col_amount_msat(stmt, "msatoshi_local"); + cc->msat_to_us_min = db_col_amount_msat(stmt, "msatoshi_to_us_min"); + cc->msat_to_us_max = db_col_amount_msat(stmt, "msatoshi_to_us_max"); /* last_tx is null for stub channels used for recovering funds through * Static channel backups. */ if (!db_col_is_null(stmt, "last_tx")) @@ -2789,7 +2786,7 @@ static bool wallet_stmt2htlc_in(struct channel *channel, in->dbid = db_col_u64(stmt, "id"); in->key.id = db_col_u64(stmt, "channel_htlc_id"); in->key.channel = channel; - db_col_amount_msat(stmt, "msatoshi", &in->msat); + in->msat = db_col_amount_msat(stmt, "msatoshi"); in->cltv_expiry = db_col_int(stmt, "cltv_expiry"); in->hstate = db_col_int(stmt, "hstate"); in->status = NULL; @@ -2862,7 +2859,7 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, out->dbid = db_col_u64(stmt, "id"); out->key.id = db_col_u64(stmt, "channel_htlc_id"); out->key.channel = channel; - db_col_amount_msat(stmt, "msatoshi", &out->msat); + out->msat = db_col_amount_msat(stmt, "msatoshi"); out->cltv_expiry = db_col_int(stmt, "cltv_expiry"); out->hstate = db_col_int(stmt, "hstate"); db_col_sha256(stmt, "payment_hash", &out->payment_hash); @@ -2887,7 +2884,7 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, out->failmsg = db_col_arr(out, stmt, "localfailmsg", u8); out->in = NULL; - db_col_amount_msat(stmt, "fees_msat", &out->fees); + out->fees = db_col_amount_msat(stmt, "fees_msat"); if (!db_col_is_null(stmt, "origin_htlc")) { u64 in_id = db_col_u64(stmt, "origin_htlc"); @@ -3300,87 +3297,99 @@ void wallet_payment_delete(struct wallet *wallet, db_exec_prepared_v2(take(stmt)); } -static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, - struct db_stmt *stmt) +struct wallet_payment *wallet_payment_new(const tal_t *ctx, + u64 dbid, + u32 timestamp, + const u32 *completed_at, + const struct sha256 *payment_hash, + u64 partid, + u64 groupid, + enum payment_status status, + /* The destination may not be known if we used `sendonion` */ + const struct node_id *destination, + struct amount_msat msatoshi, + struct amount_msat msatoshi_sent, + struct amount_msat total_msat, + /* If and only if PAYMENT_COMPLETE */ + const struct preimage *payment_preimage, + const struct secret *path_secrets, + const struct node_id *route_nodes, + const struct short_channel_id *route_channels, + const char *invstring, + const char *label, + const char *description, + const u8 *failonion, + const struct sha256 *local_invreq_id) { struct wallet_payment *payment = tal(ctx, struct wallet_payment); - payment->id = db_col_u64(stmt, "id"); - payment->status = db_col_int(stmt, "status"); - payment->destination = db_col_optional(payment, stmt, "destination", - node_id); - db_col_amount_msat(stmt, "msatoshi", &payment->msatoshi); - db_col_sha256(stmt, "payment_hash", &payment->payment_hash); - - payment->timestamp = db_col_int(stmt, "timestamp"); - payment->payment_preimage = db_col_optional(payment, stmt, - "payment_preimage", - preimage); - - /* We either used `sendpay` or `sendonion` with the `shared_secrets` - * argument. */ - if (!db_col_is_null(stmt, "path_secrets")) - payment->path_secrets - = db_col_secret_arr(payment, stmt, "path_secrets"); - else - payment->path_secrets = NULL; - - /* Either none, or both are set */ - assert(db_col_is_null(stmt, "route_nodes") - == db_col_is_null(stmt, "route_channels")); - if (!db_col_is_null(stmt, "route_nodes")) { - payment->route_nodes - = db_col_node_id_arr(payment, stmt, "route_nodes"); - payment->route_channels = - db_col_short_channel_id_arr(payment, stmt, "route_channels"); - } else { - payment->route_nodes = NULL; - payment->route_channels = NULL; - } - - db_col_amount_msat(stmt, "msatoshi_sent", &payment->msatoshi_sent); - - if (!db_col_is_null(stmt, "description")) - payment->label = db_col_strdup(payment, stmt, "description"); - 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 - payment->invstring = NULL; - if (!db_col_is_null(stmt, "failonionreply")) - payment->failonion - = db_col_arr(payment, stmt, "failonionreply", u8); - else - payment->failonion = NULL; + payment->id = dbid; + payment->status = status; + payment->timestamp = timestamp; + payment->payment_hash = *payment_hash; + payment->partid = partid; + payment->groupid = groupid; + payment->status = status; + payment->msatoshi = msatoshi; + payment->msatoshi_sent = msatoshi_sent; + payment->total_msat = total_msat; + + /* Optional fields */ + payment->completed_at = tal_dup_or_null(payment, u32, completed_at); + payment->destination = tal_dup_or_null(payment, struct node_id, destination); + payment->payment_preimage = tal_dup_or_null(payment, struct preimage, payment_preimage); + payment->path_secrets = tal_dup_talarr(payment, struct secret, path_secrets); + payment->route_nodes = tal_dup_talarr(payment, struct node_id, route_nodes); + payment->route_channels = tal_dup_talarr(payment, struct short_channel_id, route_channels); + payment->invstring = tal_strdup_or_null(payment, invstring); + payment->label = tal_strdup_or_null(payment, label); + payment->description = tal_strdup_or_null(payment, description); + payment->failonion = tal_dup_talarr(payment, u8, failonion); + payment->local_invreq_id = tal_dup_or_null(payment, struct sha256, local_invreq_id); - if (!db_col_is_null(stmt, "total_msat")) - db_col_amount_msat(stmt, "total_msat", &payment->total_msat); - else - payment->total_msat = AMOUNT_MSAT(0); + return payment; +} - if (!db_col_is_null(stmt, "partid")) - payment->partid = db_col_u64(stmt, "partid"); - else - payment->partid = 0; +static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, + struct db_stmt *stmt) +{ + struct wallet_payment *payment; + u32 *completed_at; + struct sha256 payment_hash; - payment->local_invreq_id = db_col_optional(payment, stmt, - "local_invreq_id", sha256); + db_col_sha256(stmt, "payment_hash", &payment_hash); if (!db_col_is_null(stmt, "completed_at")) { - payment->completed_at = tal(payment, u32); - *payment->completed_at = db_col_int(stmt, "completed_at"); + completed_at = tal(tmpctx, u32); + *completed_at = db_col_int(stmt, "completed_at"); } else - payment->completed_at = NULL; - - payment->groupid = db_col_u64(stmt, "groupid"); + completed_at = NULL; + + payment = wallet_payment_new(ctx, + db_col_u64(stmt, "id"), + db_col_int(stmt, "timestamp"), + completed_at, + &payment_hash, + db_col_is_null(stmt, "partid") ? 0 : db_col_u64(stmt, "partid"), + db_col_u64(stmt, "groupid"), + payment_status_in_db(db_col_int(stmt, "status")), + take(db_col_optional(NULL, stmt, "destination", node_id)), + db_col_amount_msat(stmt, "msatoshi"), + db_col_amount_msat(stmt, "msatoshi_sent"), + db_col_amount_msat(stmt, "total_msat"), + take(db_col_optional(NULL, stmt, "payment_preimage", preimage)), + take(db_col_secret_arr(NULL, stmt, "path_secrets")), + take(db_col_node_id_arr(NULL, stmt, "route_nodes")), + take(db_col_short_channel_id_arr(NULL, stmt, "route_channels")), + take(db_col_strdup_optional(NULL, stmt, "bolt11")), + take(db_col_strdup_optional(NULL, stmt, "description")), + take(db_col_strdup_optional(NULL, stmt, "paydescription")), + take(db_col_arr(NULL, stmt, "failonionreply", u8)), + take(db_col_optional(NULL, stmt, "local_invreq_id", sha256))); + /* Either none, or both are set */ + assert(db_col_is_null(stmt, "route_nodes") + == db_col_is_null(stmt, "route_channels")); return payment; } @@ -4064,7 +4073,7 @@ struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, else op->spendheight = db_col_int(stmt, "spendheight"); op->scriptpubkey = db_col_arr(op, stmt, "scriptpubkey", u8); - db_col_amount_sat(stmt, "satoshis", &op->sat); + op->sat = db_col_amount_sat(stmt, "satoshis"); tal_free(stmt); return op; @@ -4551,7 +4560,7 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) res = db_step(stmt); assert(res); - db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)", &total); + total = db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)"); tal_free(stmt); deleted = amount_msat(db_get_intvar(w->db, "deleted_forward_fees", 0)); @@ -4651,10 +4660,10 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, tal_resize(&results, count+1); struct forwarding *cur = &results[count]; cur->status = db_col_int(stmt, "state"); - db_col_amount_msat(stmt, "in_msatoshi", &cur->msat_in); + cur->msat_in = db_col_amount_msat(stmt, "in_msatoshi"); if (!db_col_is_null(stmt, "out_msatoshi")) { - db_col_amount_msat(stmt, "out_msatoshi", &cur->msat_out); + cur->msat_out = db_col_amount_msat(stmt, "out_msatoshi"); if (!amount_msat_sub(&cur->fee, cur->msat_in, cur->msat_out)) { log_broken(w->log, "Forwarded in %s less than out %s!", type_to_string(tmpctx, struct amount_msat, @@ -4760,7 +4769,7 @@ bool wallet_forward_delete(struct wallet *w, if (db_step(stmt)) { struct amount_msat deleted; - db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)", &deleted); + deleted = db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)"); deleted.millisatoshis += /* Raw: db access */ db_get_intvar(w->db, "deleted_forward_fees", 0); db_set_intvar(w->db, "deleted_forward_fees", @@ -4879,7 +4888,7 @@ struct penalty_base *wallet_penalty_base_load_for_channel(const tal_t *ctx, pb.commitment_num = db_col_u64(stmt, "commitnum"); db_col_txid(stmt, "txid", &pb.txid); pb.outnum = db_col_int(stmt, "outnum"); - db_col_amount_sat(stmt, "amount", &pb.amount); + pb.amount = db_col_amount_sat(stmt, "amount"); tal_arr_expand(&res, pb); } tal_free(stmt); @@ -5465,7 +5474,7 @@ struct wallet_htlc_iter *wallet_htlcs_next(struct wallet *w, *owner = REMOTE; else *owner = LOCAL; - db_col_amount_msat(iter->stmt, "h.msatoshi", msat); + *msat = db_col_amount_msat(iter->stmt, "h.msatoshi"); db_col_sha256(iter->stmt, "h.payment_hash", payment_hash); *cltv_expiry = db_col_int(iter->stmt, "h.cltv_expiry"); *hstate = db_col_int(iter->stmt, "h.hstate"); diff --git a/wallet/wallet.h b/wallet/wallet.h index f09e4b4efc5a..c0eb150e6bd8 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -373,6 +373,30 @@ struct wallet_payment { struct sha256 *local_invreq_id; }; +struct wallet_payment *wallet_payment_new(const tal_t *ctx, + u64 dbid, + u32 timestamp, + const u32 *completed_at, + const struct sha256 *payment_hash, + u64 partid, + u64 groupid, + enum payment_status status, + /* The destination may not be known if we used `sendonion` */ + const struct node_id *destination TAKES, + struct amount_msat msatoshi, + struct amount_msat msatoshi_sent, + struct amount_msat total_msat, + /* If and only if PAYMENT_COMPLETE */ + const struct preimage *payment_preimage TAKES, + const struct secret *path_secrets TAKES, + const struct node_id *route_nodes TAKES, + const struct short_channel_id *route_channels TAKES, + const char *invstring TAKES, + const char *label TAKES, + const char *description TAKES, + const u8 *failonion TAKES, + const struct sha256 *local_invreq_id); + struct outpoint { struct bitcoin_outpoint outpoint; u32 blockheight;