mirror of
https://github.com/aljazceru/lightning.git
synced 2026-01-07 08:04:23 +01:00
DEVELOPER: allow fetchinvoice to force they payer_secret.
We usually assume we're fetching an invoice we are going to pay, so we look up the previous payment for the payer key, and other sanity checks. This adds a developer option to fetchinvoice, which allows it to force its own payer key, which it uses to sign directly and bypasses these checks. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
neil saitug
parent
d4c441f1d7
commit
a29847f0c6
@@ -99,7 +99,11 @@ void merkle_tlv(const struct tlv_field *fields, struct sha256 *merkle)
|
||||
arr[n++] = merkle_pair(s, lnall);
|
||||
}
|
||||
|
||||
*merkle = merkle_recurse(arr, n);
|
||||
/* This should never happen, but define it as lnall in this case */
|
||||
if (n == 0)
|
||||
*merkle = lnall;
|
||||
else
|
||||
*merkle = merkle_recurse(arr, n);
|
||||
tal_free(arr);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,19 +62,26 @@ static struct sent *find_sent(const struct pubkey *blinding)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *field_diff_(const tal_t *a, const tal_t *b,
|
||||
static const char *field_diff_(struct plugin *plugin,
|
||||
const tal_t *a, const tal_t *b,
|
||||
const char *fieldname)
|
||||
{
|
||||
/* One is set and the other isn't? */
|
||||
if ((a == NULL) != (b == NULL))
|
||||
if ((a == NULL) != (b == NULL)) {
|
||||
plugin_log(plugin, LOG_DBG, "field_diff %s: a is %s, b is %s",
|
||||
fieldname, a ? "set": "unset", b ? "set": "unset");
|
||||
return fieldname;
|
||||
if (!memeq(a, tal_bytelen(a), b, tal_bytelen(b)))
|
||||
}
|
||||
if (!memeq(a, tal_bytelen(a), b, tal_bytelen(b))) {
|
||||
plugin_log(plugin, LOG_DBG, "field_diff %s: a=%s, b=%s",
|
||||
fieldname, tal_hex(tmpctx, a), tal_hex(tmpctx, b));
|
||||
return fieldname;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define field_diff(a, b, fieldname) \
|
||||
field_diff_(a->fieldname, b->fieldname, #fieldname)
|
||||
#define field_diff(a, b, fieldname) \
|
||||
field_diff_((cmd)->plugin, a->fieldname, b->fieldname, #fieldname)
|
||||
|
||||
/* Returns true if b is a with something appended. */
|
||||
static bool description_is_appended(const char *a, const char *b)
|
||||
@@ -824,6 +831,66 @@ static struct command_result *invreq_done(struct command *cmd,
|
||||
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
|
||||
}
|
||||
|
||||
/* If they hand us the payer secret, we sign it directly, bypassing checks
|
||||
* about periods etc. */
|
||||
static struct command_result *
|
||||
force_payer_secret(struct command *cmd,
|
||||
struct sent *sent,
|
||||
struct tlv_invoice_request *invreq,
|
||||
const struct secret *payer_secret)
|
||||
{
|
||||
struct sha256 merkle, sha;
|
||||
bool try_connect;
|
||||
secp256k1_keypair kp;
|
||||
u8 *msg;
|
||||
const u8 *p;
|
||||
size_t len;
|
||||
|
||||
if (secp256k1_keypair_create(secp256k1_ctx, &kp, payer_secret->data) != 1)
|
||||
return command_fail(cmd, LIGHTNINGD, "Bad payer_secret");
|
||||
|
||||
invreq->payer_key = tal(invreq, struct pubkey32);
|
||||
/* Docs say this only happens if arguments are invalid! */
|
||||
if (secp256k1_keypair_xonly_pub(secp256k1_ctx,
|
||||
&invreq->payer_key->pubkey, NULL,
|
||||
&kp) != 1)
|
||||
plugin_err(cmd->plugin,
|
||||
"secp256k1_keypair_pub failed on %s?",
|
||||
type_to_string(tmpctx, struct secret, payer_secret));
|
||||
|
||||
/* Linearize populates ->fields */
|
||||
msg = tal_arr(tmpctx, u8, 0);
|
||||
towire_invoice_request(&msg, invreq);
|
||||
p = msg;
|
||||
len = tal_bytelen(msg);
|
||||
sent->invreq = tlv_invoice_request_new(cmd);
|
||||
if (!fromwire_invoice_request(&p, &len, sent->invreq))
|
||||
plugin_err(cmd->plugin,
|
||||
"Could not remarshall invreq %s", tal_hex(tmpctx, msg));
|
||||
|
||||
merkle_tlv(sent->invreq->fields, &merkle);
|
||||
sighash_from_merkle("invoice_request", "payer_signature", &merkle, &sha);
|
||||
|
||||
sent->invreq->payer_signature = tal(invreq, struct bip340sig);
|
||||
if (!secp256k1_schnorrsig_sign(secp256k1_ctx,
|
||||
sent->invreq->payer_signature->u8,
|
||||
sha.u.u8,
|
||||
&kp,
|
||||
NULL, NULL)) {
|
||||
return command_fail(cmd, LIGHTNINGD,
|
||||
"Failed to sign with payer_secret");
|
||||
}
|
||||
|
||||
sent->path = path_to_node(sent, get_gossmap(cmd->plugin),
|
||||
sent->offer->node_id,
|
||||
&try_connect);
|
||||
if (try_connect)
|
||||
return connect_direct(cmd, &sent->path[0],
|
||||
sendinvreq_after_connect, sent);
|
||||
|
||||
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
|
||||
}
|
||||
|
||||
/* Fetches an invoice for this offer, and makes sure it corresponds. */
|
||||
static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||
const char *buffer,
|
||||
@@ -834,6 +901,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||
struct out_req *req;
|
||||
struct tlv_invoice_request *invreq;
|
||||
struct sent *sent = tal(cmd, struct sent);
|
||||
struct secret *payer_secret = NULL;
|
||||
u32 *timeout;
|
||||
|
||||
invreq = tlv_invoice_request_new(sent);
|
||||
@@ -848,6 +916,9 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||
p_opt("recurrence_label", param_string, &rec_label),
|
||||
p_opt_def("timeout", param_number, &timeout, 60),
|
||||
p_opt("payer_note", param_string, &payer_note),
|
||||
#if DEVELOPER
|
||||
p_opt("payer_secret", param_secret, &payer_secret),
|
||||
#endif
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
@@ -964,8 +1035,8 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||
}
|
||||
|
||||
/* recurrence_label uniquely identifies this series of
|
||||
* payments. */
|
||||
if (!rec_label)
|
||||
* payments (unless they supply secret themselves)! */
|
||||
if (!rec_label && !payer_secret)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"needs recurrence_label");
|
||||
} else {
|
||||
@@ -1004,6 +1075,11 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||
payer_note, strlen(payer_note),
|
||||
0);
|
||||
|
||||
/* They can provide a secret, and we don't assume it's our job
|
||||
* to pay. */
|
||||
if (payer_secret)
|
||||
return force_payer_secret(cmd, sent, invreq, payer_secret);
|
||||
|
||||
/* Make the invoice request (fills in payer_key and payer_info) */
|
||||
req = jsonrpc_request_start(cmd->plugin, cmd, "createinvoicerequest",
|
||||
&invreq_done,
|
||||
|
||||
@@ -254,6 +254,13 @@ static struct command_result *check_period(struct command *cmd,
|
||||
if (err)
|
||||
return err;
|
||||
period_idx += *ir->invreq->recurrence_start;
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST set (or not set) `recurrence_start` exactly as the
|
||||
* invoice_request did.
|
||||
*/
|
||||
ir->inv->recurrence_start
|
||||
= tal_dup(ir->inv, u32, ir->invreq->recurrence_start);
|
||||
} else {
|
||||
/* BOLT-offers #12:
|
||||
*
|
||||
@@ -734,10 +741,15 @@ static struct command_result *listoffers_done(struct command *cmd,
|
||||
* - otherwise (the offer had no `recurrence`):
|
||||
* - MUST fail the request if there is a `recurrence_counter`
|
||||
* field.
|
||||
* - MUST fail the request if there is a `recurrence_start`
|
||||
* field.
|
||||
*/
|
||||
err = invreq_must_not_have(cmd, ir, recurrence_counter);
|
||||
if (err)
|
||||
return err;
|
||||
err = invreq_must_not_have(cmd, ir, recurrence_start);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
ir->inv = tlv_invoice_new(cmd);
|
||||
@@ -770,6 +782,7 @@ static struct command_result *listoffers_done(struct command *cmd,
|
||||
*/
|
||||
if (ir->offer->quantity_min || ir->offer->quantity_max)
|
||||
ir->inv->quantity = tal_dup(ir->inv, u64, ir->invreq->quantity);
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - MUST set `payer_key` exactly as the invoice_request did.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user