Browse Source

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>
ppa
Rusty Russell 4 years ago
committed by Christian Decker
parent
commit
43b71de897
  1. 221
      lightningd/offer.c
  2. 2
      wire/Makefile
  3. 2
      wire/common_wiregen.c
  4. 2
      wire/common_wiregen.h
  5. 2
      wire/onion_printgen.c
  6. 2
      wire/onion_printgen.h
  7. 2
      wire/onion_wiregen.c
  8. 2
      wire/onion_wiregen.h
  9. 2
      wire/peer_printgen.c
  10. 2
      wire/peer_printgen.h
  11. 2
      wire/peer_wiregen.c
  12. 2
      wire/peer_wiregen.h

221
lightningd/offer.c

@ -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);

2
wire/Makefile

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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…
Cancel
Save