Browse Source

sendpay: optional argument to link local offer.

This is for offers which have `send_invoice`: we need to associate the
payment with the original offer, in (the usual) case where it is a single
use offer.  We mark it used when it's paid, to avoid a race.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa
Rusty Russell 4 years ago
committed by Christian Decker
parent
commit
3f4683e3f8
  1. 1
      common/jsonrpc_errors.h
  2. 87
      lightningd/pay.c
  3. 2
      wallet/db.c
  4. 74
      wallet/wallet.c
  5. 10
      wallet/wallet.h

1
common/jsonrpc_errors.h

@ -41,6 +41,7 @@ static const errcode_t PAY_NO_SUCH_PAYMENT = 208;
static const errcode_t PAY_UNSPECIFIED_ERROR = 209;
static const errcode_t PAY_STOPPED_RETRYING = 210;
static const errcode_t PAY_STATUS_UNEXPECTED = 211;
static const errcode_t PAY_OFFER_INVALID = 212;
/* `fundchannel` or `withdraw` errors */
static const errcode_t FUND_MAX_EXCEEDED = 300;

87
lightningd/pay.c

@ -334,6 +334,10 @@ void payment_succeeded(struct lightningd *ld, struct htlc_out *hout,
hout->partid);
assert(payment);
#if EXPERIMENTAL_FEATURES
if (payment->local_offer_id)
wallet_offer_mark_used(ld->wallet->db, payment->local_offer_id);
#endif
tell_waiters_success(ld, &hout->payment_hash, payment);
}
@ -789,6 +793,60 @@ static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld,
&dont_care_about_channel_update);
}
#if EXPERIMENTAL_FEATURES
static struct command_result *check_offer_usage(struct command *cmd,
const struct sha256 *local_offer_id)
{
enum offer_status status;
const struct wallet_payment **payments;
if (!local_offer_id)
return NULL;
if (!wallet_offer_find(tmpctx, cmd->ld->wallet, local_offer_id,
NULL, &status))
return command_fail(cmd, PAY_OFFER_INVALID,
"Unknown offer %s",
type_to_string(tmpctx, struct sha256,
local_offer_id));
if (!offer_status_active(status))
return command_fail(cmd, PAY_OFFER_INVALID,
"Inactive offer %s",
type_to_string(tmpctx, struct sha256,
local_offer_id));
if (!offer_status_single(status))
return NULL;
/* OK, we must not attempt more than one payment at once for
* single_use offer */
payments = wallet_payments_by_offer(tmpctx, cmd->ld->wallet, local_offer_id);
for (size_t i = 0; i < tal_count(payments); i++) {
switch (payments[i]->status) {
case PAYMENT_COMPLETE:
return command_fail(cmd, PAY_OFFER_INVALID,
"Single-use offer already paid"
" with %s",
type_to_string(tmpctx, struct sha256,
&payments[i]
->payment_hash));
case PAYMENT_PENDING:
return command_fail(cmd, PAY_OFFER_INVALID,
"Single-use offer already"
" in progress with %s",
type_to_string(tmpctx, struct sha256,
&payments[i]
->payment_hash));
case PAYMENT_FAILED:
break;
}
}
return NULL;
}
#endif /* EXPERIMENTAL_FEATURES */
/* destination/route_channels/route_nodes are NULL (and path_secrets may be NULL)
* if we're sending a raw onion. */
static struct command_result *
@ -805,7 +863,8 @@ send_payment_core(struct lightningd *ld,
const struct node_id *destination,
struct node_id *route_nodes TAKES,
struct short_channel_id *route_channels TAKES,
struct secret *path_secrets)
struct secret *path_secrets,
const struct sha256 *local_offer_id)
{
const struct wallet_payment **payments, *old_payment = NULL;
struct channel *channel;
@ -942,6 +1001,13 @@ send_payment_core(struct lightningd *ld,
&total_msat));
}
#if EXPERIMENTAL_FEATURES
struct command_result *offer_err;
offer_err = check_offer_usage(cmd, local_offer_id);
if (offer_err)
return offer_err;
#endif
channel = active_channel_by_id(ld, &first_hop->nodeid, NULL);
if (!channel) {
struct json_stream *data
@ -1016,6 +1082,10 @@ send_payment_core(struct lightningd *ld,
payment->bolt11 = tal_strdup(payment, b11str);
else
payment->bolt11 = NULL;
if (local_offer_id)
payment->local_offer_id = tal_dup(payment, struct sha256, local_offer_id);
else
payment->local_offer_id = NULL;
/* We write this into db when HTLC is actually sent. */
wallet_payment_setup(ld->wallet, payment);
@ -1034,6 +1104,7 @@ send_payment(struct lightningd *ld,
struct amount_msat total_msat,
const char *label TAKES,
const char *b11str TAKES,
const struct sha256 *local_offer_id,
const struct secret *payment_secret)
{
unsigned int base_expiry;
@ -1117,7 +1188,7 @@ send_payment(struct lightningd *ld,
return send_payment_core(ld, cmd, rhash, partid, &route[0],
msat, total_msat, label, b11str,
packet, &ids[n_hops - 1], ids,
channels, path_secrets);
channels, path_secrets, local_offer_id);
}
static struct command_result *
@ -1202,6 +1273,7 @@ static struct command_result *json_sendonion(struct command *cmd,
struct secret *path_secrets;
struct amount_msat *msat;
u64 *partid;
struct sha256 *local_offer_id = NULL;
if (!param(cmd, buffer, params,
p_req("onion", param_bin_from_hex, &onion),
@ -1213,6 +1285,9 @@ static struct command_result *json_sendonion(struct command *cmd,
p_opt("bolt11", param_string, &b11str),
p_opt_def("msatoshi", param_msat, &msat, AMOUNT_MSAT(0)),
p_opt("destination", param_node_id, &destination),
#if EXPERIMENTAL_FEATURES
p_opt("local_offer_id", param_sha256, &local_offer_id),
#endif
NULL))
return command_param_failed();
@ -1227,7 +1302,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,
path_secrets);
path_secrets, local_offer_id);
}
static const struct json_command sendonion_command = {
@ -1353,6 +1428,7 @@ static struct command_result *json_sendpay(struct command *cmd,
const char *b11str, *label;
u64 *partid;
struct secret *payment_secret;
struct sha256 *local_offer_id = NULL;
/* For generating help, give new-style. */
if (!param(cmd, buffer, params,
@ -1363,6 +1439,9 @@ static struct command_result *json_sendpay(struct command *cmd,
p_opt("bolt11", param_string, &b11str),
p_opt("payment_secret", param_secret, &payment_secret),
p_opt_def("partid", param_u64, &partid, 0),
#if EXPERIMENTAL_FEATURES
p_opt("local_offer_id", param_sha256, &local_offer_id),
#endif
NULL))
return command_param_failed();
@ -1406,7 +1485,7 @@ static struct command_result *json_sendpay(struct command *cmd,
route,
final_amount,
msat ? *msat : final_amount,
label, b11str, payment_secret);
label, b11str, local_offer_id, payment_secret);
}
static const struct json_command sendpay_command = {

2
wallet/db.c

@ -666,6 +666,8 @@ static struct migration dbmigrations[] = {
");"), NULL},
/* A reference into our own offers table, if it was made from one */
{SQL("ALTER TABLE invoices ADD COLUMN local_offer_id BLOB DEFAULT NULL;"), NULL},
/* A reference into our own offers table, if it was made from one */
{SQL("ALTER TABLE payments ADD COLUMN local_offer_id BLOB DEFAULT NULL;"), NULL},
};
/* Leak tracking. */

74
wallet/wallet.c

@ -2494,8 +2494,9 @@ void wallet_payment_store(struct wallet *wallet,
" description,"
" bolt11,"
" total_msat,"
" partid"
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
" partid,"
" local_offer_id"
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
db_bind_int(stmt, 0, payment->status);
db_bind_sha256(stmt, 1, &payment->payment_hash);
@ -2537,6 +2538,11 @@ void wallet_payment_store(struct wallet *wallet,
db_bind_amount_msat(stmt, 11, &payment->total_msat);
db_bind_u64(stmt, 12, payment->partid);
if (payment->local_offer_id != NULL)
db_bind_sha256(stmt, 13, payment->local_offer_id);
else
db_bind_null(stmt, 13);
db_exec_prepared_v2(stmt);
payment->id = db_last_insert_id_v2(stmt);
assert(payment->id > 0);
@ -2657,6 +2663,12 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx,
else
payment->partid = 0;
if (!db_column_is_null(stmt, 16)) {
payment->local_offer_id = tal(payment, struct sha256);
db_column_sha256(stmt, 16, payment->local_offer_id);
} else
payment->local_offer_id = NULL;
return payment;
}
@ -2690,6 +2702,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet,
", failonionreply"
", total_msat"
", partid"
", local_offer_id"
" FROM payments"
" WHERE payment_hash = ?"
" AND partid = ?"));
@ -2917,6 +2930,7 @@ wallet_payment_list(const tal_t *ctx,
", failonionreply"
", total_msat"
", partid"
", local_offer_id"
" FROM payments"
" WHERE payment_hash = ?;"));
db_bind_sha256(stmt, 0, payment_hash);
@ -2938,6 +2952,7 @@ wallet_payment_list(const tal_t *ctx,
", failonionreply"
", total_msat"
", partid"
", local_offer_id"
" FROM payments"
" ORDER BY id;"));
}
@ -2960,6 +2975,57 @@ wallet_payment_list(const tal_t *ctx,
return payments;
}
const struct wallet_payment **
wallet_payments_by_offer(const tal_t *ctx,
struct wallet *wallet,
const struct sha256 *local_offer_id)
{
const struct wallet_payment **payments;
struct db_stmt *stmt;
struct wallet_payment *p;
size_t i;
payments = tal_arr(ctx, const struct wallet_payment *, 0);
stmt = db_prepare_v2(wallet->db, SQL("SELECT"
" id"
", status"
", destination"
", msatoshi"
", payment_hash"
", timestamp"
", payment_preimage"
", path_secrets"
", route_nodes"
", route_channels"
", msatoshi_sent"
", description"
", bolt11"
", failonionreply"
", total_msat"
", partid"
", local_offer_id"
" FROM payments"
" WHERE local_offer_id = ?;"));
db_bind_sha256(stmt, 0, local_offer_id);
db_query_prepared(stmt);
for (i = 0; db_step(stmt); i++) {
tal_resize(&payments, i+1);
payments[i] = wallet_stmt2payment(payments, stmt);
}
tal_free(stmt);
/* Now attach payments not yet in db. */
list_for_each(&wallet->unstored_payments, p, list) {
if (!p->local_offer_id || !sha256_eq(p->local_offer_id, local_offer_id))
continue;
tal_resize(&payments, i+1);
payments[i++] = p;
}
return payments;
}
void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id,
const struct bitcoin_signature *htlc_sigs)
{
@ -4083,8 +4149,8 @@ static void offer_status_update(struct db *db,
stmt = db_prepare_v2(db, SQL("UPDATE invoices"
" SET state=?"
" WHERE state=? AND local_offer_id = ?;"));
db_bind_int(stmt, 0, invoice_status_in_db(UNPAID));
db_bind_int(stmt, 1, invoice_status_in_db(EXPIRED));
db_bind_int(stmt, 0, invoice_status_in_db(EXPIRED));
db_bind_int(stmt, 1, invoice_status_in_db(UNPAID));
db_bind_sha256(stmt, 2, offer_id);
db_exec_prepared_v2(take(stmt));
}

10
wallet/wallet.h

@ -244,6 +244,9 @@ struct wallet_payment {
/* If we could not decode the fail onion, just add it here. */
const u8 *failonion;
/* If we are associated with an internal offer */
struct sha256 *local_offer_id;
};
struct outpoint {
@ -1078,6 +1081,13 @@ const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
struct wallet *wallet,
const struct sha256 *payment_hash);
/**
* wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id
*/
const struct wallet_payment **wallet_payments_by_offer(const tal_t *ctx,
struct wallet *wallet,
const struct sha256 *local_offer_id);
/**
* wallet_htlc_sigs_save - Store the latest HTLC sigs for the channel
*/

Loading…
Cancel
Save