Skip to content

Commit

Permalink
lightningd: Add signinvoice to sign a BOLT11 invoice.
Browse files Browse the repository at this point in the history
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.

Changlog-Added: JSON-RPC: `signinvoice` new command to sign BOLT11
invoices.
  • Loading branch information
dongcarl committed Nov 7, 2022
1 parent bc709ba commit 6ca1cdc
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Core Lightning Documentation
lightning-sendpsbt <lightning-sendpsbt.7.md>
lightning-setchannel <lightning-setchannel.7.md>
lightning-setchannelfee <lightning-setchannelfee.7.md>
lightning-signinvoice <lightning-signinvoice.7.md>
lightning-signmessage <lightning-signmessage.7.md>
lightning-signpsbt <lightning-signpsbt.7.md>
lightning-staticbackup <lightning-staticbackup.7.md>
Expand Down
47 changes: 47 additions & 0 deletions doc/lightning-signinvoice.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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)
[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:9fc2c7cb6e5980774a768dd9ddfe81e254c084554c159e6b07e92e703dc10595)
14 changes: 14 additions & 0 deletions doc/schemas/signinvoice.request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": [
"invstring"
],
"properties": {
"invstring": {
"type": "string",
"description": ""
}
}
}
14 changes: 14 additions & 0 deletions doc/schemas/signinvoice.schema.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
53 changes: 53 additions & 0 deletions lightningd/invoice.c
Original file line number Diff line number Diff line change
Expand Up @@ -1788,3 +1788,56 @@ static const struct json_command createinvoice_command = {
};

AUTODATA(json_command, &createinvoice_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;
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);

if (!b11->description)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Missing description in invoice");

if (!b11->expiry)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Missing expiry 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);
13 changes: 13 additions & 0 deletions tests/test_invoices.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,19 @@ def test_waitanyinvoice(node_factory, executor):
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):
"""Test waiting for invoices, where they are paid in reverse order
to when they are created.
Expand Down

0 comments on commit 6ca1cdc

Please sign in to comment.