lightningd: Add signinvoice to sign a BOLT11 invoice.

Though there's already a `createinvoice` command, there are usecases where a
user may want to sign an invoice that they don't yet have the preimage to. For
example, they may have an htlc_accepted plugin that pays to obtain the preimage
from someone else and returns a `{ "result": "resolve", ... }`.

This RPC command addresses this usecase without overly complicating the
semantics of the existing `createinvoice` command.

Changelog-Added: JSON-RPC: `signinvoice` new command to sign BOLT11
invoices.
This commit is contained in:
Carl Dong
2022-11-06 19:45:47 -05:00
committed by Alex Myers
parent f0b8544eba
commit 1dbc29b8c0
7 changed files with 149 additions and 0 deletions

View File

@@ -84,6 +84,7 @@ MANPAGES := doc/lightning-cli.1 \
doc/lightning-sendpay.7 \ doc/lightning-sendpay.7 \
doc/lightning-setchannel.7 \ doc/lightning-setchannel.7 \
doc/lightning-sendcustommsg.7 \ doc/lightning-sendcustommsg.7 \
doc/lightning-signinvoice.7 \
doc/lightning-signmessage.7 \ doc/lightning-signmessage.7 \
doc/lightning-staticbackup.7 \ doc/lightning-staticbackup.7 \
doc/lightning-txprepare.7 \ doc/lightning-txprepare.7 \

View File

@@ -116,6 +116,7 @@ Core Lightning Documentation
lightning-sendpay <lightning-sendpay.7.md> lightning-sendpay <lightning-sendpay.7.md>
lightning-sendpsbt <lightning-sendpsbt.7.md> lightning-sendpsbt <lightning-sendpsbt.7.md>
lightning-setchannel <lightning-setchannel.7.md> lightning-setchannel <lightning-setchannel.7.md>
lightning-signinvoice <lightning-signinvoice.7.md>
lightning-signmessage <lightning-signmessage.7.md> lightning-signmessage <lightning-signmessage.7.md>
lightning-signpsbt <lightning-signpsbt.7.md> lightning-signpsbt <lightning-signpsbt.7.md>
lightning-sql <lightning-sql.7.md> lightning-sql <lightning-sql.7.md>

View File

@@ -0,0 +1,51 @@
lightning-signinvoice -- Low-level invoice signing
=====================================================
SYNOPSIS
--------
**signinvoice** *invstring*
DESCRIPTION
-----------
The **signinvoice** RPC command signs an invoice. Unlike
**createinvoice** it does not save the invoice into the database and
thus does not require the preimage.
The *invstring* parameter is of bolt11 form, but the final signature
is ignored. Minimal sanity checks are done.
RETURN VALUE
------------
[comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object is returned, containing:
- **bolt11** (string): the bolt11 string
[comment]: # (GENERATE-FROM-SCHEMA-END)
On failure, an error is returned.
The following error codes may occur:
- -1: Catchall nonspecific error.
AUTHOR
------
Carl Dong <<contact@carldong.me>> is mainly responsible.
SEE ALSO
--------
lightning-createinvoice(7), lightning-invoice(7), lightning-listinvoices(7),
lightning-delinvoice(7), lightning-getroute(7), lightning-sendpay(7),
lightning-offer(7).
RESOURCES
---------
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:9348784bd3daaed1cd35b29b2e5c91ea17bc8e11bf5bb6e1de9a098241cb74d6)

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"added": "v23.02",
"required": [
"invstring"
],
"properties": {
"invstring": {
"type": "string",
"description": ""
}
}
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": [
"bolt11"
],
"properties": {
"bolt11": {
"type": "string",
"description": "the bolt11 string"
}
}
}

View File

@@ -1897,3 +1897,57 @@ static const struct json_command preapprovekeysend_command = {
"Ask the HSM to preapprove a keysend payment." "Ask the HSM to preapprove a keysend payment."
}; };
AUTODATA(json_command, &preapprovekeysend_command); AUTODATA(json_command, &preapprovekeysend_command);
static struct command_result *json_signinvoice(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
const char *invstring;
struct json_stream *response;
struct bolt11 *b11;
struct sha256 hash;
const u5 *sig;
bool have_n;
char *fail;
if (!param(cmd, buffer, params,
p_req("invstring", param_string, &invstring),
NULL))
return command_param_failed();
b11 = bolt11_decode_nosig(cmd, invstring, cmd->ld->our_features,
NULL, chainparams, &hash, &sig, &have_n,
&fail);
if (!b11)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Unparsable invoice '%s': %s",
invstring, fail);
/* This adds the signature */
char *b11enc = bolt11_encode(cmd, b11, have_n,
hsm_sign_b11, cmd->ld);
/* BOLT #11:
* A writer:
*...
* - MUST include either exactly one `d` or exactly one `h` field.
*/
if (!b11->description && !b11->description_hash)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Missing description in invoice");
response = json_stream_success(cmd);
json_add_invstring(response, b11enc);
return command_success(cmd, response);
}
static const struct json_command signinvoice_command = {
"signinvoice",
"payment",
json_signinvoice,
"Lowlevel command to sign invoice {invstring}."
};
AUTODATA(json_command, &signinvoice_command);

View File

@@ -533,6 +533,19 @@ def test_waitanyinvoice(node_factory, executor):
l2.rpc.waitanyinvoice('non-number') l2.rpc.waitanyinvoice('non-number')
def test_signinvoice(node_factory, executor):
# Setup
l1, l2 = node_factory.line_graph(2)
# Create an invoice for l1
inv1 = l1.rpc.invoice(1000, 'inv1', 'inv1')['bolt11']
assert l1.rpc.decodepay(inv1)['payee'] == l1.info['id']
# Have l2 re-sign the invoice
inv2 = l2.rpc.signinvoice(inv1)['bolt11']
assert l1.rpc.decodepay(inv2)['payee'] == l2.info['id']
def test_waitanyinvoice_reversed(node_factory, executor): def test_waitanyinvoice_reversed(node_factory, executor):
"""Test waiting for invoices, where they are paid in reverse order """Test waiting for invoices, where they are paid in reverse order
to when they are created. to when they are created.