mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
lightningd: low-level createinvoicerequest API (EXPERIMENTAL_FEATURES)
This is similar to the createinvoice API, except we don't need to save invoice requests in the database. We may, however, have to look up payment_key for recurring invoice requests, and sign the message with the payment_key. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
59efd160c1
commit
43b71de897
@ -206,3 +206,224 @@ static const struct json_command disableoffer_command = {
|
||||
"Disable offer {offer_id}",
|
||||
};
|
||||
AUTODATA(json_command, &disableoffer_command);
|
||||
|
||||
/* We do some sanity checks now, since we're looking up prev payment anyway,
|
||||
* but our main purpose is to fill in invreq->payer_info tweak. */
|
||||
static struct command_result *prev_payment(struct command *cmd,
|
||||
const char *label,
|
||||
struct tlv_invoice_request *invreq)
|
||||
{
|
||||
const struct wallet_payment **payments;
|
||||
bool prev_paid = false;
|
||||
|
||||
assert(!invreq->payer_info);
|
||||
payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL);
|
||||
|
||||
for (size_t i = 0; i < tal_count(payments); i++) {
|
||||
const struct tlv_invoice *inv;
|
||||
char *fail;
|
||||
|
||||
/* FIXME: Restrict db queries instead */
|
||||
if (!payments[i]->label || !streq(label, payments[i]->label))
|
||||
continue;
|
||||
|
||||
if (!payments[i]->invstring)
|
||||
continue;
|
||||
|
||||
inv = invoice_decode(tmpctx, payments[i]->invstring,
|
||||
strlen(payments[i]->invstring),
|
||||
NULL, chainparams, &fail);
|
||||
if (!inv)
|
||||
continue;
|
||||
|
||||
/* They can reuse labels across different offers. */
|
||||
if (!sha256_eq(inv->offer_id, invreq->offer_id))
|
||||
continue;
|
||||
|
||||
/* Be paranoid, in case someone inserts their own
|
||||
* clashing label! */
|
||||
if (!inv->recurrence_counter)
|
||||
continue;
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - if the offer contained `recurrence_base` with
|
||||
* `start_any_period` non-zero:
|
||||
* - MUST include `recurrence_start`
|
||||
* - MUST set `period_offset` to the period the sender wants
|
||||
* for the initial request
|
||||
* - MUST set `period_offset` to the same value on all
|
||||
* following requests.
|
||||
*/
|
||||
if (invreq->recurrence_start) {
|
||||
if (!inv->recurrence_start)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"unexpected"
|
||||
" recurrence_start");
|
||||
if (*inv->recurrence_start != *invreq->recurrence_start)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"recurrence_start was"
|
||||
" previously %u",
|
||||
*inv->recurrence_start);
|
||||
} else {
|
||||
if (inv->recurrence_start)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"missing"
|
||||
" recurrence_start");
|
||||
}
|
||||
|
||||
if (*inv->recurrence_counter == *invreq->recurrence_counter-1) {
|
||||
if (payments[i]->status == PAYMENT_COMPLETE)
|
||||
prev_paid = true;
|
||||
}
|
||||
|
||||
if (inv->payer_info)
|
||||
invreq->payer_info
|
||||
= tal_dup_talarr(invreq, u8, inv->payer_info);
|
||||
}
|
||||
|
||||
if (!invreq->payer_info)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"No previous payment attempted for this"
|
||||
" label and offer");
|
||||
|
||||
if (!prev_paid)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"previous invoice has not been paid");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct command_result *param_b12_invreq(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
struct tlv_invoice_request **invreq)
|
||||
{
|
||||
char *fail;
|
||||
|
||||
*invreq = invrequest_decode(cmd, buffer + tok->start,
|
||||
tok->end - tok->start,
|
||||
cmd->ld->our_features, chainparams, &fail);
|
||||
if (!*invreq)
|
||||
return command_fail_badparam(cmd, name, buffer, tok, fail);
|
||||
if ((*invreq)->payer_info)
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"must not have payer_info");
|
||||
if ((*invreq)->payer_key)
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"must not have payer_key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct command_result *json_createinvoicerequest(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct tlv_invoice_request *invreq;
|
||||
const char *label;
|
||||
struct sha256 tweakhash;
|
||||
struct json_stream *response;
|
||||
secp256k1_pubkey tweaked;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("bolt12", param_b12_invreq, &invreq),
|
||||
p_opt("recurrence_label", param_escaped_string, &label),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
if (invreq->recurrence_counter) {
|
||||
if (!label)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Need payment label for recurring payments");
|
||||
|
||||
if (*invreq->recurrence_counter != 0) {
|
||||
struct command_result *err
|
||||
= prev_payment(cmd, label, invreq);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!invreq->payer_info) {
|
||||
/* BOLT-offers #12:
|
||||
* `payer_info` might typically contain information about the
|
||||
* derivation of the `payer_key`. This should not leak any
|
||||
* information (such as using a simple BIP-32 derivation
|
||||
* path); a valid system might be for a node to maintain a
|
||||
* base payer key, and encode a 128-bit tweak here. The
|
||||
* payer_key would be derived by tweaking the base key with
|
||||
* SHA256(payer_base_pubkey || tweak).
|
||||
*/
|
||||
invreq->payer_info = tal_arr(invreq, u8, 16);
|
||||
randombytes_buf(invreq->payer_info,
|
||||
tal_bytelen(invreq->payer_info));
|
||||
}
|
||||
|
||||
payer_key_tweak(&cmd->ld->bolt12_base,
|
||||
invreq->payer_info, tal_bytelen(invreq->payer_info),
|
||||
&tweakhash);
|
||||
|
||||
/* Tweaking gives a not-x-only pubkey, must then convert. */
|
||||
if (secp256k1_xonly_pubkey_tweak_add(secp256k1_ctx,
|
||||
&tweaked,
|
||||
&cmd->ld->bolt12_base.pubkey,
|
||||
tweakhash.u.u8) != 1) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Invalid tweak");
|
||||
}
|
||||
invreq->payer_key = tal(invreq, struct pubkey32);
|
||||
if (secp256k1_xonly_pubkey_from_pubkey(secp256k1_ctx,
|
||||
&invreq->payer_key->pubkey,
|
||||
NULL, &tweaked) != 1) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Invalid tweaked key");
|
||||
}
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - if the offer contained `recurrence`:
|
||||
*...
|
||||
* - MUST set `recurrence_signature` `sig` as detailed in
|
||||
* [Signature Calculation](#signature-calculation) using the
|
||||
* `payer_key`.
|
||||
*/
|
||||
if (invreq->recurrence_counter) {
|
||||
struct sha256 merkle;
|
||||
u8 *msg;
|
||||
|
||||
/* This populates the ->fields from our entries */
|
||||
invreq->fields = tlv_make_fields(invreq, invoice_request);
|
||||
merkle_tlv(invreq->fields, &merkle);
|
||||
|
||||
msg = towire_hsmd_sign_bolt12(NULL,
|
||||
"invoice_request",
|
||||
"recurrence_signature",
|
||||
&merkle, invreq->payer_info);
|
||||
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg)))
|
||||
fatal("Could not write to HSM: %s", strerror(errno));
|
||||
|
||||
msg = wire_sync_read(tmpctx, cmd->ld->hsm_fd);
|
||||
invreq->recurrence_signature = tal(invreq, struct bip340sig);
|
||||
if (!fromwire_hsmd_sign_bolt12_reply(msg,
|
||||
invreq->recurrence_signature))
|
||||
fatal("HSM gave bad sign_offer_reply %s",
|
||||
tal_hex(msg, msg));
|
||||
|
||||
/* FIXME: Validate signature! */
|
||||
}
|
||||
|
||||
response = json_stream_success(cmd);
|
||||
json_add_string(response, "bolt12", invrequest_encode(tmpctx, invreq));
|
||||
if (label)
|
||||
json_add_escaped_string(response, "recurrence_label",
|
||||
take(json_escape(NULL, label)));
|
||||
return command_success(cmd, response);
|
||||
}
|
||||
|
||||
static const struct json_command createinvreq_command = {
|
||||
"createinvoicerequest",
|
||||
"payment",
|
||||
json_createinvoicerequest,
|
||||
"Create and sign an invoice_request {bolt12}, with {recurrence_label} if recurring, filling in payer_info and payer_key."
|
||||
};
|
||||
AUTODATA(json_command, &createinvreq_command);
|
||||
|
@ -94,7 +94,7 @@ wire/peer_exp_wiregen.c_args := $(wire/peer_wiregen.c_args)
|
||||
wire/onion_exp_wiregen.h_args := $(wire/onion_wiregen.h_args)
|
||||
wire/onion_exp_wiregen.c_args := $(wire/onion_wiregen.c_args)
|
||||
|
||||
wire/bolt12_exp_wiregen.c_args := -s --expose-tlv-type=blinded_path
|
||||
wire/bolt12_exp_wiregen.c_args := -s --expose-tlv-type=blinded_path --expose-tlv-type=invoice_request
|
||||
wire/bolt12_exp_wiregen.h_args := --include='bitcoin/short_channel_id.h' --include='bitcoin/signature.h' --include='bitcoin/privkey.h' --include='common/bigsize.h' --include='common/amount.h' --include='common/node_id.h' --include='bitcoin/block.h' --include='wire/onion_wire.h' $(wire/bolt12_exp_wiregen.c_args)
|
||||
|
||||
wire/peer_wiregen.h_args := --include='common/channel_id.h' --include='bitcoin/tx.h' --include='bitcoin/preimage.h' --include='bitcoin/short_channel_id.h' --include='common/node_id.h' --include='common/bigsize.h' --include='bitcoin/block.h' --include='bitcoin/privkey.h' -s --expose-tlv-type=n1 --expose-tlv-type=n2
|
||||
|
2
wire/common_wiregen.c
generated
2
wire/common_wiregen.c
generated
@ -100,4 +100,4 @@ bool fromwire_custommsg_out(const tal_t *ctx, const void *p, u8 **msg)
|
||||
fromwire_u8_array(&cursor, &plen, *msg, msg_len);
|
||||
return cursor != NULL;
|
||||
}
|
||||
// SHA256STAMP:18fdc0626e07a8b9b4d2eb82b765aafc45798a3337e38946967f947dfd94fc28
|
||||
// SHA256STAMP:3d4dd8c4e467b5a2158181d3b2e1e7f59f5082c8c5c91ba35d1fdda9a8d1ed6e
|
||||
|
2
wire/common_wiregen.h
generated
2
wire/common_wiregen.h
generated
@ -41,4 +41,4 @@ bool fromwire_custommsg_out(const tal_t *ctx, const void *p, u8 **msg);
|
||||
|
||||
|
||||
#endif /* LIGHTNING_WIRE_COMMON_WIREGEN_H */
|
||||
// SHA256STAMP:18fdc0626e07a8b9b4d2eb82b765aafc45798a3337e38946967f947dfd94fc28
|
||||
// SHA256STAMP:3d4dd8c4e467b5a2158181d3b2e1e7f59f5082c8c5c91ba35d1fdda9a8d1ed6e
|
||||
|
2
wire/onion_printgen.c
generated
2
wire/onion_printgen.c
generated
@ -653,4 +653,4 @@ void printonion_wire_tlv_message(const char *tlv_name, const u8 *msg) {
|
||||
printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_tlv_payload, ARRAY_SIZE(print_tlvs_tlv_payload));
|
||||
}
|
||||
}
|
||||
// SHA256STAMP:76ca7e25b0fce4262e163356666de98f2b4b271a6bda0931ff22df17c6d93a35
|
||||
// SHA256STAMP:de983a18d6a298f6dc27229bc10eaaf846bc313e06450e23d122ea84de5c829a
|
||||
|
2
wire/onion_printgen.h
generated
2
wire/onion_printgen.h
generated
@ -57,4 +57,4 @@ void printwire_mpp_timeout(const char *fieldname, const u8 *cursor);
|
||||
|
||||
|
||||
#endif /* LIGHTNING_WIRE_ONION_PRINTGEN_H */
|
||||
// SHA256STAMP:76ca7e25b0fce4262e163356666de98f2b4b271a6bda0931ff22df17c6d93a35
|
||||
// SHA256STAMP:de983a18d6a298f6dc27229bc10eaaf846bc313e06450e23d122ea84de5c829a
|
||||
|
2
wire/onion_wiregen.c
generated
2
wire/onion_wiregen.c
generated
@ -697,4 +697,4 @@ bool fromwire_mpp_timeout(const void *p)
|
||||
return false;
|
||||
return cursor != NULL;
|
||||
}
|
||||
// SHA256STAMP:76ca7e25b0fce4262e163356666de98f2b4b271a6bda0931ff22df17c6d93a35
|
||||
// SHA256STAMP:de983a18d6a298f6dc27229bc10eaaf846bc313e06450e23d122ea84de5c829a
|
||||
|
2
wire/onion_wiregen.h
generated
2
wire/onion_wiregen.h
generated
@ -207,4 +207,4 @@ bool fromwire_mpp_timeout(const void *p);
|
||||
|
||||
|
||||
#endif /* LIGHTNING_WIRE_ONION_WIREGEN_H */
|
||||
// SHA256STAMP:76ca7e25b0fce4262e163356666de98f2b4b271a6bda0931ff22df17c6d93a35
|
||||
// SHA256STAMP:de983a18d6a298f6dc27229bc10eaaf846bc313e06450e23d122ea84de5c829a
|
||||
|
2
wire/peer_printgen.c
generated
2
wire/peer_printgen.c
generated
@ -2036,4 +2036,4 @@ void printpeer_wire_tlv_message(const char *tlv_name, const u8 *msg) {
|
||||
printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_reply_channel_range_tlvs, ARRAY_SIZE(print_tlvs_reply_channel_range_tlvs));
|
||||
}
|
||||
}
|
||||
// SHA256STAMP:91aa8c2e798052d38eefeab6a13563b213358e508b772bf7b221186d7c9d08ef
|
||||
// SHA256STAMP:7cb56bd1ecb24076a620bf103cd28fc31cd45a31e2d6e59a1fd1f2a742d520fd
|
||||
|
2
wire/peer_printgen.h
generated
2
wire/peer_printgen.h
generated
@ -70,4 +70,4 @@ void printwire_gossip_timestamp_filter(const char *fieldname, const u8 *cursor);
|
||||
void printwire_channel_update_checksums(const char *fieldname, const u8 **cursor, size_t *plen);
|
||||
void printwire_channel_update_timestamps(const char *fieldname, const u8 **cursor, size_t *plen);
|
||||
#endif /* LIGHTNING_WIRE_PEER_PRINTGEN_H */
|
||||
// SHA256STAMP:91aa8c2e798052d38eefeab6a13563b213358e508b772bf7b221186d7c9d08ef
|
||||
// SHA256STAMP:7cb56bd1ecb24076a620bf103cd28fc31cd45a31e2d6e59a1fd1f2a742d520fd
|
||||
|
2
wire/peer_wiregen.c
generated
2
wire/peer_wiregen.c
generated
@ -1630,4 +1630,4 @@ bool fromwire_channel_update_option_channel_htlc_max(const void *p, secp256k1_ec
|
||||
*htlc_maximum_msat = fromwire_amount_msat(&cursor, &plen);
|
||||
return cursor != NULL;
|
||||
}
|
||||
// SHA256STAMP:91aa8c2e798052d38eefeab6a13563b213358e508b772bf7b221186d7c9d08ef
|
||||
// SHA256STAMP:7cb56bd1ecb24076a620bf103cd28fc31cd45a31e2d6e59a1fd1f2a742d520fd
|
||||
|
2
wire/peer_wiregen.h
generated
2
wire/peer_wiregen.h
generated
@ -595,4 +595,4 @@ bool fromwire_channel_update_option_channel_htlc_max(const void *p, secp256k1_ec
|
||||
|
||||
|
||||
#endif /* LIGHTNING_WIRE_PEER_WIREGEN_H */
|
||||
// SHA256STAMP:91aa8c2e798052d38eefeab6a13563b213358e508b772bf7b221186d7c9d08ef
|
||||
// SHA256STAMP:7cb56bd1ecb24076a620bf103cd28fc31cd45a31e2d6e59a1fd1f2a742d520fd
|
||||
|
Loading…
Reference in New Issue
Block a user