Skip to content

Commit

Permalink
DEVELOPER: allow fetchinvoice to force they payer_secret.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rustyrussell committed Jul 3, 2021
1 parent e11f943 commit 4558609
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 3 deletions.
6 changes: 5 additions & 1 deletion common/bolt12_merkle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
73 changes: 71 additions & 2 deletions plugins/fetchinvoice.c
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,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);

invreq->payer_signature = tal(invreq, struct bip340sig);
if (!secp256k1_schnorrsig_sign(secp256k1_ctx,
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,
Expand All @@ -834,6 +894,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);
Expand All @@ -848,6 +909,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();

Expand Down Expand Up @@ -964,8 +1028,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 {
Expand Down Expand Up @@ -1004,6 +1068,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,
Expand Down

0 comments on commit 4558609

Please sign in to comment.