mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
db: add invoicerequests table.
We no longer use offers for "I want to send you money", but we'll use invoice_requests directly. Create a new table for them, and associated functions. The "localofferid" for "pay" and "sendpay" is now "localinvreqid". This is an experimental-only option, so document the change under experimental only. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-EXPERIMENTAL: JSON-RPC: `pay` and `sendpay` `localofferid` is now `localinvreqid`.
This commit is contained in:
parent
891cef7b2b
commit
02d7454226
@ -874,6 +874,7 @@
|
||||
"Pay.exclude": 10,
|
||||
"Pay.exemptfee": 7,
|
||||
"Pay.label": 3,
|
||||
"Pay.localinvreqid": 14,
|
||||
"Pay.localofferid": 9,
|
||||
"Pay.maxdelay": 6,
|
||||
"Pay.maxfee": 11,
|
||||
@ -913,6 +914,7 @@
|
||||
"SendOnion.first_hop": 2,
|
||||
"SendOnion.groupid": 11,
|
||||
"SendOnion.label": 4,
|
||||
"SendOnion.localinvreqid": 13,
|
||||
"SendOnion.localofferid": 10,
|
||||
"SendOnion.msatoshi": 8,
|
||||
"SendOnion.onion": 1,
|
||||
@ -940,6 +942,7 @@
|
||||
"SendPay.bolt11": 5,
|
||||
"SendPay.groupid": 9,
|
||||
"SendPay.label": 3,
|
||||
"SendPay.localinvreqid": 11,
|
||||
"SendPay.localofferid": 8,
|
||||
"SendPay.msatoshi": 4,
|
||||
"SendPay.partid": 7,
|
||||
|
BIN
cln-grpc/proto/node.proto
generated
BIN
cln-grpc/proto/node.proto
generated
Binary file not shown.
BIN
cln-grpc/src/convert.rs
generated
BIN
cln-grpc/src/convert.rs
generated
Binary file not shown.
BIN
cln-rpc/src/model.rs
generated
BIN
cln-rpc/src/model.rs
generated
Binary file not shown.
@ -45,7 +45,7 @@ enum jsonrpc_errcode {
|
||||
PAY_UNSPECIFIED_ERROR = 209,
|
||||
PAY_STOPPED_RETRYING = 210,
|
||||
PAY_STATUS_UNEXPECTED = 211,
|
||||
PAY_OFFER_INVALID = 212,
|
||||
PAY_INVOICE_REQUEST_INVALID = 212,
|
||||
|
||||
/* `fundchannel` or `withdraw` errors */
|
||||
FUND_MAX_EXCEEDED = 300,
|
||||
|
@ -1073,7 +1073,7 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
|
||||
def pay(self, bolt11, amount_msat=None, label=None, riskfactor=None,
|
||||
maxfeepercent=None, retry_for=None,
|
||||
maxdelay=None, exemptfee=None, localofferid=None, exclude=None,
|
||||
maxdelay=None, exemptfee=None, localinvreqid=None, exclude=None,
|
||||
maxfee=None, description=None, msatoshi=None):
|
||||
"""
|
||||
Send payment specified by {bolt11} with {amount_msat}
|
||||
@ -1092,7 +1092,7 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
"retry_for": retry_for,
|
||||
"maxdelay": maxdelay,
|
||||
"exemptfee": exemptfee,
|
||||
"localofferid": localofferid,
|
||||
"localinvreqid": localinvreqid,
|
||||
"exclude": exclude,
|
||||
"maxfee": maxfee,
|
||||
"description": description,
|
||||
|
BIN
contrib/pyln-testing/pyln/testing/node_pb2.py
generated
BIN
contrib/pyln-testing/pyln/testing/node_pb2.py
generated
Binary file not shown.
@ -6,7 +6,7 @@ SYNOPSIS
|
||||
|
||||
**pay** *bolt11* [*msatoshi*] [*label*] [*riskfactor*]
|
||||
[*maxfeepercent*] [*retry_for*] [*maxdelay*] [*exemptfee*]
|
||||
[*localofferid*] [*exclude*] [*maxfee*] [*description*]
|
||||
[*localinvreqid*] [*exclude*] [*maxfee*] [*description*]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -32,8 +32,8 @@ leveraged by forwarding nodes. Setting `exemptfee` allows the
|
||||
`maxfeepercent` check to be skipped on fees that are smaller than
|
||||
`exemptfee` (default: 5000 millisatoshi).
|
||||
|
||||
`localofferid` is used by offers to link a payment attempt to a local
|
||||
`send_invoice` offer created by lightningd-offerout(7). This ensures
|
||||
`localinvreqid` is used by offers to link a payment attempt to a local
|
||||
`invoice_request` offer created by lightningd-invoicerequest(7). This ensures
|
||||
that we only make a single payment for an offer, and that the offer is
|
||||
marked `used` once paid.
|
||||
|
||||
|
@ -5,7 +5,7 @@ SYNOPSIS
|
||||
--------
|
||||
|
||||
**sendpay** *route* *payment\_hash* [*label*] [*msatoshi*]
|
||||
[*bolt11*] [*payment_secret*] [*partid*] [*localofferid*] [*groupid*]
|
||||
[*bolt11*] [*payment_secret*] [*partid*] [*localinvreqid*] [*groupid*]
|
||||
[*payment_metadata*] [*description*]
|
||||
|
||||
DESCRIPTION
|
||||
@ -45,9 +45,9 @@ partial payments with the same *payment_hash*. The *msatoshi* amount
|
||||
*payment_hash* must be equal, and **sendpay** will fail if there are
|
||||
already *msatoshi* worth of payments pending.
|
||||
|
||||
The *localofferid* value indicates that this payment is being made for a local
|
||||
send_invoice offer: this ensures that we only send a payment for a single-use
|
||||
offer once.
|
||||
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
|
||||
invoice_request once.
|
||||
|
||||
*groupid* allows you to attach a number which appears in **listsendpays** so
|
||||
payments can be identified as part of a logical group. The *pay* plugin uses
|
||||
@ -109,7 +109,7 @@ The following error codes may occur:
|
||||
will be routing failure object.
|
||||
- 204: Failure along route; retry a different route. The *data* field
|
||||
of the error will be routing failure object.
|
||||
- 212: *localofferid* refers to an invalid, or used, local offer.
|
||||
- 212: *localinvreqid* refers to an invalid, or used, local invoice_request.
|
||||
|
||||
A routing failure object has the fields below:
|
||||
- *erring\_index*. The index of the node along the route that reported
|
||||
|
@ -30,7 +30,7 @@
|
||||
"exemptfee": {
|
||||
"type": "msat"
|
||||
},
|
||||
"localofferid": {
|
||||
"localinvreqid": {
|
||||
"type": "hex"
|
||||
},
|
||||
"exclude": {
|
||||
|
@ -54,7 +54,7 @@
|
||||
"destination": {
|
||||
"type": "pubkey"
|
||||
},
|
||||
"localofferid": {
|
||||
"localinvreqid": {
|
||||
"type": "hash"
|
||||
},
|
||||
"groupid": {
|
||||
|
@ -54,7 +54,7 @@
|
||||
"partid": {
|
||||
"type": "u16"
|
||||
},
|
||||
"localofferid": {
|
||||
"localinvreqid": {
|
||||
"type": "hex"
|
||||
},
|
||||
"groupid": {
|
||||
|
@ -341,8 +341,9 @@ void payment_succeeded(struct lightningd *ld, struct htlc_out *hout,
|
||||
hout->partid, hout->groupid);
|
||||
assert(payment);
|
||||
|
||||
if (payment->local_offer_id)
|
||||
wallet_offer_mark_used(ld->wallet->db, payment->local_offer_id);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -777,46 +778,48 @@ static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld,
|
||||
blinding, partid, groupid, onion, NULL, hout);
|
||||
}
|
||||
|
||||
static struct command_result *check_offer_usage(struct command *cmd,
|
||||
const struct sha256 *local_offer_id)
|
||||
static struct command_result *check_invoice_request_usage(struct command *cmd,
|
||||
const struct sha256 *local_invreq_id)
|
||||
{
|
||||
enum offer_status status;
|
||||
const struct wallet_payment **payments;
|
||||
|
||||
if (!local_offer_id)
|
||||
if (!local_invreq_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",
|
||||
if (!wallet_invoice_request_find(tmpctx, cmd->ld->wallet,
|
||||
local_invreq_id,
|
||||
NULL, &status))
|
||||
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
||||
"Unknown invoice_request %s",
|
||||
type_to_string(tmpctx, struct sha256,
|
||||
local_offer_id));
|
||||
local_invreq_id));
|
||||
|
||||
if (!offer_status_active(status))
|
||||
return command_fail(cmd, PAY_OFFER_INVALID,
|
||||
"Inactive offer %s",
|
||||
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
||||
"Inactive invoice_request %s",
|
||||
type_to_string(tmpctx, struct sha256,
|
||||
local_offer_id));
|
||||
local_invreq_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);
|
||||
* single_use invoice_request we publish! */
|
||||
payments = wallet_payments_by_invoice_request(tmpctx, cmd->ld->wallet,
|
||||
local_invreq_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"
|
||||
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
||||
"Single-use invoice_request 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"
|
||||
return command_fail(cmd, PAY_INVOICE_REQUEST_INVALID,
|
||||
"Single-use invoice_request already"
|
||||
" in progress with %s",
|
||||
type_to_string(tmpctx, struct sha256,
|
||||
&payments[i]
|
||||
@ -872,7 +875,7 @@ send_payment_core(struct lightningd *ld,
|
||||
struct node_id *route_nodes TAKES,
|
||||
struct short_channel_id *route_channels TAKES,
|
||||
struct secret *path_secrets,
|
||||
const struct sha256 *local_offer_id)
|
||||
const struct sha256 *local_invreq_id)
|
||||
{
|
||||
const struct wallet_payment **payments, *old_payment = NULL;
|
||||
struct channel *channel;
|
||||
@ -881,6 +884,7 @@ send_payment_core(struct lightningd *ld,
|
||||
struct routing_failure *fail;
|
||||
struct amount_msat msat_already_pending = AMOUNT_MSAT(0);
|
||||
bool have_complete = false;
|
||||
struct command_result *invreq_err;
|
||||
|
||||
/* Now, do we already have one or more payments? */
|
||||
payments = wallet_payment_list(tmpctx, ld->wallet, rhash);
|
||||
@ -1037,10 +1041,9 @@ send_payment_core(struct lightningd *ld,
|
||||
&total_msat));
|
||||
}
|
||||
|
||||
struct command_result *offer_err;
|
||||
offer_err = check_offer_usage(cmd, local_offer_id);
|
||||
if (offer_err)
|
||||
return offer_err;
|
||||
invreq_err = check_invoice_request_usage(cmd, local_invreq_id);
|
||||
if (invreq_err)
|
||||
return invreq_err;
|
||||
|
||||
channel = find_channel_for_htlc_add(ld, &first_hop->node_id,
|
||||
&first_hop->scid);
|
||||
@ -1117,8 +1120,8 @@ send_payment_core(struct lightningd *ld,
|
||||
payment->description = tal_strdup(payment, description);
|
||||
else
|
||||
payment->description = NULL;
|
||||
payment->local_offer_id = tal_dup_or_null(payment, struct sha256,
|
||||
local_offer_id);
|
||||
payment->local_invreq_id = tal_dup_or_null(payment, struct sha256,
|
||||
local_invreq_id);
|
||||
|
||||
/* We write this into db when HTLC is actually sent. */
|
||||
wallet_payment_setup(ld->wallet, payment);
|
||||
@ -1139,7 +1142,7 @@ send_payment(struct lightningd *ld,
|
||||
const char *label TAKES,
|
||||
const char *invstring TAKES,
|
||||
const char *description TAKES,
|
||||
const struct sha256 *local_offer_id,
|
||||
const struct sha256 *local_invreq_id,
|
||||
const struct secret *payment_secret,
|
||||
const u8 *payment_metadata)
|
||||
{
|
||||
@ -1212,7 +1215,7 @@ send_payment(struct lightningd *ld,
|
||||
msat, total_msat,
|
||||
label, invstring, description,
|
||||
packet, &ids[n_hops - 1], ids,
|
||||
channels, path_secrets, local_offer_id);
|
||||
channels, path_secrets, local_invreq_id);
|
||||
}
|
||||
|
||||
static struct command_result *
|
||||
@ -1286,7 +1289,7 @@ static struct command_result *json_sendonion(struct command *cmd,
|
||||
struct secret *path_secrets;
|
||||
struct amount_msat *msat;
|
||||
u64 *partid, *group;
|
||||
struct sha256 *local_offer_id = NULL;
|
||||
struct sha256 *local_invreq_id = NULL;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("onion", param_bin_from_hex, &onion),
|
||||
@ -1299,7 +1302,7 @@ static struct command_result *json_sendonion(struct command *cmd,
|
||||
p_opt("bolt11", param_string, &invstring),
|
||||
p_opt_def("amount_msat|msatoshi", param_msat, &msat, AMOUNT_MSAT(0)),
|
||||
p_opt("destination", param_node_id, &destination),
|
||||
p_opt("localofferid", param_sha256, &local_offer_id),
|
||||
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
||||
p_opt("groupid", param_u64, &group),
|
||||
p_opt("description", param_string, &description),
|
||||
NULL))
|
||||
@ -1325,7 +1328,7 @@ static struct command_result *json_sendonion(struct command *cmd,
|
||||
first_hop, *msat, AMOUNT_MSAT(0),
|
||||
label, invstring, description,
|
||||
packet, destination, NULL, NULL,
|
||||
path_secrets, local_offer_id);
|
||||
path_secrets, local_invreq_id);
|
||||
}
|
||||
|
||||
static const struct json_command sendonion_command = {
|
||||
@ -1412,7 +1415,7 @@ static struct command_result *json_sendpay(struct command *cmd,
|
||||
const char *invstring, *label, *description;
|
||||
u64 *partid, *group;
|
||||
struct secret *payment_secret;
|
||||
struct sha256 *local_offer_id;
|
||||
struct sha256 *local_invreq_id;
|
||||
u8 *payment_metadata;
|
||||
|
||||
/* For generating help, give new-style. */
|
||||
@ -1425,7 +1428,7 @@ static struct command_result *json_sendpay(struct command *cmd,
|
||||
p_opt("bolt11", param_string, &invstring),
|
||||
p_opt("payment_secret", param_secret, &payment_secret),
|
||||
p_opt_def("partid", param_u64, &partid, 0),
|
||||
p_opt("localofferid", param_sha256, &local_offer_id),
|
||||
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
||||
p_opt("groupid", param_u64, &group),
|
||||
p_opt("payment_metadata", param_bin_from_hex, &payment_metadata),
|
||||
p_opt("description", param_string, &description),
|
||||
@ -1478,7 +1481,7 @@ static struct command_result *json_sendpay(struct command *cmd,
|
||||
route,
|
||||
final_amount,
|
||||
msat ? *msat : final_amount,
|
||||
label, invstring, description, local_offer_id,
|
||||
label, invstring, description, local_invreq_id,
|
||||
payment_secret, payment_metadata);
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,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;
|
||||
p->local_invreq_id = parent->local_invreq_id;
|
||||
p->groupid = parent->groupid;
|
||||
p->invstring = parent->invstring;
|
||||
p->description = parent->description;
|
||||
@ -107,7 +107,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
|
||||
p->description = NULL;
|
||||
/* Caller must set this. */
|
||||
p->local_id = NULL;
|
||||
p->local_offer_id = NULL;
|
||||
p->local_invreq_id = NULL;
|
||||
p->groupid = 0;
|
||||
}
|
||||
|
||||
@ -1599,8 +1599,8 @@ static struct command_result *payment_createonion_success(struct command *cmd,
|
||||
if (p->destination)
|
||||
json_add_node_id(req->js, "destination", p->destination);
|
||||
|
||||
if (p->local_offer_id)
|
||||
json_add_sha256(req->js, "localofferid", p->local_offer_id);
|
||||
if (p->local_invreq_id)
|
||||
json_add_sha256(req->js, "localinvreqid", p->local_invreq_id);
|
||||
|
||||
send_outreq(p->plugin, req);
|
||||
return command_still_pending(cmd);
|
||||
|
@ -277,9 +277,9 @@ struct payment {
|
||||
/* Description, usually set if bolt11 has only description_hash */
|
||||
const char *description;
|
||||
|
||||
/* If this is paying a local offer, this is the one (sendpay ensures we
|
||||
* don't pay twice for single-use offers) */
|
||||
struct sha256 *local_offer_id;
|
||||
/* If this is paying a local invoice_request, this is the one (sendpay
|
||||
* ensures we don't pay twice for single-use invoice requests) */
|
||||
struct sha256 *local_invreq_id;
|
||||
|
||||
/* Textual explanation of why this payment was attempted. */
|
||||
const char *why;
|
||||
|
@ -987,7 +987,7 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
struct shadow_route_data *shadow_route;
|
||||
struct amount_msat *invmsat;
|
||||
u64 invexpiry;
|
||||
struct sha256 *local_offer_id;
|
||||
struct sha256 *local_invreq_id;
|
||||
const struct tlv_invoice *b12;
|
||||
struct out_req *req;
|
||||
struct route_exclusion **exclusions;
|
||||
@ -1011,7 +1011,7 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
p_opt_def("maxdelay", param_number, &maxdelay,
|
||||
maxdelay_default),
|
||||
p_opt("exemptfee", param_msat, &exemptfee),
|
||||
p_opt("localofferid", param_sha256, &local_offer_id),
|
||||
p_opt("localinvreqid", param_sha256, &local_invreq_id),
|
||||
p_opt("exclude", param_route_exclusion_array, &exclusions),
|
||||
p_opt("maxfee", param_msat, &maxfee),
|
||||
p_opt("description", param_string, &description),
|
||||
@ -1159,7 +1159,7 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
invexpiry = *b12->invoice_created_at + *b12->invoice_relative_expiry;
|
||||
else
|
||||
invexpiry = *b12->invoice_created_at + BOLT12_DEFAULT_REL_EXPIRY;
|
||||
p->local_offer_id = tal_steal(p, local_offer_id);
|
||||
p->local_invreq_id = tal_steal(p, local_invreq_id);
|
||||
}
|
||||
|
||||
if (time_now().ts.tv_sec > invexpiry)
|
||||
|
@ -416,7 +416,7 @@ def test_pay_plugin(node_factory):
|
||||
|
||||
# Make sure usage messages are present.
|
||||
msg = 'pay bolt11 [amount_msat] [label] [riskfactor] [maxfeepercent] '\
|
||||
'[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude] '\
|
||||
'[retry_for] [maxdelay] [exemptfee] [localinvreqid] [exclude] '\
|
||||
'[maxfee] [description]'
|
||||
if DEVELOPER:
|
||||
msg += ' [use_shadow]'
|
||||
|
10
wallet/db.c
10
wallet/db.c
@ -930,6 +930,16 @@ static struct migration dbmigrations[] = {
|
||||
{SQL("ALTER TABLE channels ADD scid BIGINT;"), migrate_channels_scids_as_integers},
|
||||
{SQL("ALTER TABLE payments ADD failscid BIGINT;"), migrate_payments_scids_as_integers},
|
||||
{SQL("ALTER TABLE outputs ADD is_in_coinbase INTEGER DEFAULT 0;"), NULL},
|
||||
{SQL("CREATE TABLE invoicerequests ("
|
||||
" invreq_id BLOB"
|
||||
", bolt12 TEXT"
|
||||
", label TEXT"
|
||||
", status INTEGER"
|
||||
", PRIMARY KEY (invreq_id)"
|
||||
");"), NULL},
|
||||
/* A reference into our own invoicerequests table, if it was made from one */
|
||||
{SQL("ALTER TABLE payments ADD COLUMN local_invreq_id BLOB DEFAULT NULL REFERENCES invoicerequests(invreq_id);"), NULL},
|
||||
/* FIXME: Remove payments local_offer_id column! */
|
||||
};
|
||||
|
||||
/* Released versions are of form v{num}[.{num}]* */
|
||||
|
200
wallet/wallet.c
200
wallet/wallet.c
@ -3094,7 +3094,7 @@ void wallet_payment_store(struct wallet *wallet,
|
||||
" bolt11,"
|
||||
" total_msat,"
|
||||
" partid,"
|
||||
" local_offer_id,"
|
||||
" local_invreq_id,"
|
||||
" groupid,"
|
||||
" paydescription"
|
||||
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
|
||||
@ -3139,8 +3139,8 @@ 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);
|
||||
if (payment->local_invreq_id != NULL)
|
||||
db_bind_sha256(stmt, 13, payment->local_invreq_id);
|
||||
else
|
||||
db_bind_null(stmt, 13);
|
||||
|
||||
@ -3285,11 +3285,11 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx,
|
||||
else
|
||||
payment->partid = 0;
|
||||
|
||||
if (!db_col_is_null(stmt, "local_offer_id")) {
|
||||
payment->local_offer_id = tal(payment, struct sha256);
|
||||
db_col_sha256(stmt, "local_offer_id", payment->local_offer_id);
|
||||
if (!db_col_is_null(stmt, "local_invreq_id")) {
|
||||
payment->local_invreq_id = tal(payment, struct sha256);
|
||||
db_col_sha256(stmt, "local_invreq_id", payment->local_invreq_id);
|
||||
} else
|
||||
payment->local_offer_id = NULL;
|
||||
payment->local_invreq_id = NULL;
|
||||
|
||||
if (!db_col_is_null(stmt, "completed_at")) {
|
||||
payment->completed_at = tal(payment, u32);
|
||||
@ -3333,7 +3333,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet,
|
||||
", failonionreply"
|
||||
", total_msat"
|
||||
", partid"
|
||||
", local_offer_id"
|
||||
", local_invreq_id"
|
||||
", groupid"
|
||||
", completed_at"
|
||||
" FROM payments"
|
||||
@ -3575,7 +3575,7 @@ wallet_payment_list(const tal_t *ctx,
|
||||
", failonionreply"
|
||||
", total_msat"
|
||||
", partid"
|
||||
", local_offer_id"
|
||||
", local_invreq_id"
|
||||
", groupid"
|
||||
", completed_at"
|
||||
" FROM payments"
|
||||
@ -3602,7 +3602,7 @@ wallet_payment_list(const tal_t *ctx,
|
||||
", failonionreply"
|
||||
", total_msat"
|
||||
", partid"
|
||||
", local_offer_id"
|
||||
", local_invreq_id"
|
||||
", groupid"
|
||||
", completed_at"
|
||||
" FROM payments"
|
||||
@ -3628,9 +3628,9 @@ wallet_payment_list(const tal_t *ctx,
|
||||
}
|
||||
|
||||
const struct wallet_payment **
|
||||
wallet_payments_by_offer(const tal_t *ctx,
|
||||
struct wallet *wallet,
|
||||
const struct sha256 *local_offer_id)
|
||||
wallet_payments_by_invoice_request(const tal_t *ctx,
|
||||
struct wallet *wallet,
|
||||
const struct sha256 *local_invreq_id)
|
||||
{
|
||||
const struct wallet_payment **payments;
|
||||
struct db_stmt *stmt;
|
||||
@ -3656,12 +3656,12 @@ wallet_payments_by_offer(const tal_t *ctx,
|
||||
", failonionreply"
|
||||
", total_msat"
|
||||
", partid"
|
||||
", local_offer_id"
|
||||
", local_invreq_id"
|
||||
", groupid"
|
||||
", completed_at"
|
||||
" FROM payments"
|
||||
" WHERE local_offer_id = ?;"));
|
||||
db_bind_sha256(stmt, 0, local_offer_id);
|
||||
" WHERE local_invreq_id = ?;"));
|
||||
db_bind_sha256(stmt, 0, local_invreq_id);
|
||||
db_query_prepared(stmt);
|
||||
|
||||
for (i = 0; db_step(stmt); i++) {
|
||||
@ -3672,7 +3672,7 @@ wallet_payments_by_offer(const tal_t *ctx,
|
||||
|
||||
/* 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))
|
||||
if (!p->local_invreq_id || !sha256_eq(p->local_invreq_id, local_invreq_id))
|
||||
continue;
|
||||
tal_resize(&payments, i+1);
|
||||
payments[i++] = p;
|
||||
@ -5130,6 +5130,172 @@ void wallet_offer_mark_used(struct db *db, const struct sha256 *offer_id)
|
||||
}
|
||||
}
|
||||
|
||||
bool wallet_invoice_request_create(struct wallet *w,
|
||||
const struct sha256 *invreq_id,
|
||||
const char *bolt12,
|
||||
const struct json_escape *label,
|
||||
enum offer_status status)
|
||||
{
|
||||
struct db_stmt *stmt;
|
||||
|
||||
assert(offer_status_active(status));
|
||||
|
||||
/* Test if already exists. */
|
||||
stmt = db_prepare_v2(w->db, SQL("SELECT 1"
|
||||
" FROM invoicerequests"
|
||||
" WHERE invreq_id = ?;"));
|
||||
db_bind_sha256(stmt, 0, invreq_id);
|
||||
db_query_prepared(stmt);
|
||||
|
||||
if (db_step(stmt)) {
|
||||
db_col_ignore(stmt, "1");
|
||||
tal_free(stmt);
|
||||
return false;
|
||||
}
|
||||
tal_free(stmt);
|
||||
|
||||
stmt = db_prepare_v2(w->db,
|
||||
SQL("INSERT INTO invoicerequests ("
|
||||
" invreq_id"
|
||||
", bolt12"
|
||||
", label"
|
||||
", status"
|
||||
") VALUES (?, ?, ?, ?);"));
|
||||
|
||||
db_bind_sha256(stmt, 0, invreq_id);
|
||||
db_bind_text(stmt, 1, bolt12);
|
||||
if (label)
|
||||
db_bind_json_escape(stmt, 2, label);
|
||||
else
|
||||
db_bind_null(stmt, 2);
|
||||
db_bind_int(stmt, 3, offer_status_in_db(status));
|
||||
db_exec_prepared_v2(take(stmt));
|
||||
return true;
|
||||
}
|
||||
|
||||
char *wallet_invoice_request_find(const tal_t *ctx,
|
||||
struct wallet *w,
|
||||
const struct sha256 *invreq_id,
|
||||
const struct json_escape **label,
|
||||
enum offer_status *status)
|
||||
{
|
||||
struct db_stmt *stmt;
|
||||
char *bolt12;
|
||||
|
||||
stmt = db_prepare_v2(w->db, SQL("SELECT bolt12, label, status"
|
||||
" FROM invoicerequests"
|
||||
" WHERE invreq_id = ?;"));
|
||||
db_bind_sha256(stmt, 0, invreq_id);
|
||||
db_query_prepared(stmt);
|
||||
|
||||
if (!db_step(stmt)) {
|
||||
tal_free(stmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bolt12 = db_col_strdup(ctx, stmt, "bolt12");
|
||||
if (label) {
|
||||
if (db_col_is_null(stmt, "label"))
|
||||
*label = NULL;
|
||||
else
|
||||
*label = db_col_json_escape(ctx, stmt, "label");
|
||||
} else
|
||||
db_col_ignore(stmt, "label");
|
||||
|
||||
if (status)
|
||||
*status = offer_status_in_db(db_col_int(stmt, "status"));
|
||||
else
|
||||
db_col_ignore(stmt, "status");
|
||||
|
||||
tal_free(stmt);
|
||||
return bolt12;
|
||||
}
|
||||
|
||||
struct db_stmt *wallet_invreq_id_first(struct wallet *w, struct sha256 *invreq_id)
|
||||
{
|
||||
struct db_stmt *stmt;
|
||||
|
||||
stmt = db_prepare_v2(w->db, SQL("SELECT invreq_id FROM invoicerequests;"));
|
||||
db_query_prepared(stmt);
|
||||
|
||||
return wallet_invreq_id_next(w, stmt, invreq_id);
|
||||
}
|
||||
|
||||
struct db_stmt *wallet_invreq_id_next(struct wallet *w,
|
||||
struct db_stmt *stmt,
|
||||
struct sha256 *invreq_id)
|
||||
{
|
||||
if (!db_step(stmt))
|
||||
return tal_free(stmt);
|
||||
|
||||
db_col_sha256(stmt, "invreq_id", invreq_id);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
/* If we make an invoice_request inactive */
|
||||
static void invoice_request_status_update(struct db *db,
|
||||
const struct sha256 *invreq_id,
|
||||
enum offer_status oldstatus,
|
||||
enum offer_status newstatus)
|
||||
{
|
||||
struct db_stmt *stmt;
|
||||
|
||||
stmt = db_prepare_v2(db, SQL("UPDATE invoicerequests"
|
||||
" SET status=?"
|
||||
" WHERE invreq_id = ?;"));
|
||||
db_bind_int(stmt, 0, offer_status_in_db(newstatus));
|
||||
db_bind_sha256(stmt, 1, invreq_id);
|
||||
db_exec_prepared_v2(take(stmt));
|
||||
}
|
||||
|
||||
enum offer_status wallet_invoice_request_disable(struct wallet *w,
|
||||
const struct sha256 *invreq_id,
|
||||
enum offer_status s)
|
||||
{
|
||||
enum offer_status newstatus;
|
||||
|
||||
assert(offer_status_active(s));
|
||||
|
||||
newstatus = offer_status_in_db(s & ~OFFER_STATUS_ACTIVE_F);
|
||||
invoice_request_status_update(w->db, invreq_id, s, newstatus);
|
||||
|
||||
return newstatus;
|
||||
}
|
||||
|
||||
void wallet_invoice_request_mark_used(struct db *db, const struct sha256 *invreq_id)
|
||||
{
|
||||
struct db_stmt *stmt;
|
||||
enum offer_status status;
|
||||
|
||||
stmt = db_prepare_v2(db, SQL("SELECT status"
|
||||
" FROM invoicerequests"
|
||||
" WHERE invreq_id = ?;"));
|
||||
db_bind_sha256(stmt, 0, invreq_id);
|
||||
db_query_prepared(stmt);
|
||||
if (!db_step(stmt))
|
||||
fatal("%s: unknown invreq_id %s",
|
||||
__func__,
|
||||
type_to_string(tmpctx, struct sha256, invreq_id));
|
||||
|
||||
status = offer_status_in_db(db_col_int(stmt, "status"));
|
||||
tal_free(stmt);
|
||||
|
||||
if (!offer_status_active(status))
|
||||
fatal("%s: invreq_id %s not active: status %i",
|
||||
__func__,
|
||||
type_to_string(tmpctx, struct sha256, invreq_id),
|
||||
status);
|
||||
|
||||
if (!offer_status_used(status)) {
|
||||
enum offer_status newstatus;
|
||||
|
||||
if (offer_status_single(status))
|
||||
newstatus = OFFER_SINGLE_USE_USED;
|
||||
else
|
||||
newstatus = OFFER_MULTIPLE_USE_USED;
|
||||
invoice_request_status_update(db, invreq_id, status, newstatus);
|
||||
}
|
||||
}
|
||||
|
||||
/* We join key parts with nuls for now. */
|
||||
static void db_bind_datastore_key(struct db_stmt *stmt,
|
||||
|
@ -368,8 +368,8 @@ 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;
|
||||
/* If we are associated with an internal invoice_request */
|
||||
struct sha256 *local_invreq_id;
|
||||
};
|
||||
|
||||
struct outpoint {
|
||||
@ -1195,11 +1195,12 @@ const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
|
||||
|
||||
|
||||
/**
|
||||
* wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id
|
||||
* wallet_payments_by_invoice_request - Retrieve a list of payments for this local_invreq_id
|
||||
*/
|
||||
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 **
|
||||
wallet_payments_by_invoice_request(const tal_t *ctx,
|
||||
struct wallet *wallet,
|
||||
const struct sha256 *local_invreq_id);
|
||||
|
||||
/**
|
||||
* wallet_htlc_sigs_save - Store the latest HTLC sigs for the channel
|
||||
@ -1583,6 +1584,85 @@ enum offer_status wallet_offer_disable(struct wallet *w,
|
||||
void wallet_offer_mark_used(struct db *db, const struct sha256 *offer_id)
|
||||
NO_NULL_ARGS;
|
||||
|
||||
/**
|
||||
* Store an offer in the database.
|
||||
* @w: the wallet
|
||||
* @invreq_id: the hash of the invoice_request.
|
||||
* @bolt12: invoice_request as text.
|
||||
* @label: optional label for this invoice_request.
|
||||
* @status: OFFER_SINGLE_USE or OFFER_MULTIPLE_USE
|
||||
*/
|
||||
bool wallet_invoice_request_create(struct wallet *w,
|
||||
const struct sha256 *invreq_id,
|
||||
const char *bolt12,
|
||||
const struct json_escape *label,
|
||||
enum offer_status status)
|
||||
NON_NULL_ARGS(1,2,3);
|
||||
|
||||
/**
|
||||
* Retrieve an invoice_request from the database.
|
||||
* @ctx: the tal context to allocate return from.
|
||||
* @w: the wallet
|
||||
* @invreq_id: the merkle root, as used for signing (must be unique)
|
||||
* @label: the label of the invoice_request, set to NULL if none (or NULL)
|
||||
* @status: set if succeeds (or NULL)
|
||||
*
|
||||
* If @invreq_id is found, returns the bolt12 text, sets @label and
|
||||
* @state. Otherwise returns NULL.
|
||||
*/
|
||||
char *wallet_invoice_request_find(const tal_t *ctx,
|
||||
struct wallet *w,
|
||||
const struct sha256 *invreq_id,
|
||||
const struct json_escape **label,
|
||||
enum offer_status *status)
|
||||
NON_NULL_ARGS(1,2,3);
|
||||
|
||||
/**
|
||||
* Iterate through all the invoice_requests.
|
||||
* @w: the wallet
|
||||
* @invreq_id: the first invoice_request id (if returns non-NULL)
|
||||
*
|
||||
* Returns pointer to hand as @stmt to wallet_invreq_id_next(), or NULL.
|
||||
* If you choose not to call wallet_invreq_id_next() you must free it!
|
||||
*/
|
||||
struct db_stmt *wallet_invreq_id_first(struct wallet *w,
|
||||
struct sha256 *invreq_id);
|
||||
|
||||
/**
|
||||
* Iterate through all the invoice_requests.
|
||||
* @w: the wallet
|
||||
* @stmt: return from wallet_invreq_id_first() or previous wallet_invreq_id_next()
|
||||
* @invreq_id: the next invoice_request id (if returns non-NULL)
|
||||
*
|
||||
* Returns NULL once we're out of invoice_requests. If you choose not to call
|
||||
* wallet_invreq_id_next() again you must free return.
|
||||
*/
|
||||
struct db_stmt *wallet_invreq_id_next(struct wallet *w,
|
||||
struct db_stmt *stmt,
|
||||
struct sha256 *invreq_id);
|
||||
|
||||
/**
|
||||
* Disable an invoice_request in the database.
|
||||
* @w: the wallet
|
||||
* @invreq_id: the merkle root, as used for signing (must be unique)
|
||||
* @s: the current status (must be active).
|
||||
*
|
||||
* Must exist. Returns new status. */
|
||||
enum offer_status wallet_invoice_request_disable(struct wallet *w,
|
||||
const struct sha256 *invreq_id,
|
||||
enum offer_status s)
|
||||
NO_NULL_ARGS;
|
||||
|
||||
/**
|
||||
* Mark an invoice_request in the database used.
|
||||
* @w: the wallet
|
||||
* @invreq_id: the merkle root, as used for signing (must be unique)
|
||||
*
|
||||
* Must exist and be active.
|
||||
*/
|
||||
void wallet_invoice_request_mark_used(struct db *db, const struct sha256 *invreq_id)
|
||||
NO_NULL_ARGS;
|
||||
|
||||
/**
|
||||
* Add an new key/value to the datastore (generation 0)
|
||||
* @w: the wallet
|
||||
|
Loading…
Reference in New Issue
Block a user