Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add createonion and sendonion RPC commands #3260

Merged
merged 23 commits into from
Dec 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f6c0d7a
json: Add two param parsers for secrets and hex-encoded binary data
cdecker Nov 7, 2019
e3de45c
sphinx: Promote TLV payloads to be non-experimental
cdecker Nov 7, 2019
8a6c5a7
json-rpc: Add `createonion` command similar to the `devtools/onion` tool
cdecker Nov 7, 2019
211d1f1
pytest: Add a test for `createonion`
cdecker Nov 7, 2019
2b84662
pay: Split the onion construction from sendpay
cdecker Nov 7, 2019
2e6e0bc
pay: Make wallet_payment->destination optional
cdecker Nov 7, 2019
a0117cd
pay: Add param_route_hop helper to parse `struct route_hop` from JSON
cdecker Nov 8, 2019
e82e9da
json-rpc: Add the `sendonion` RPC command
cdecker Nov 7, 2019
1266ae0
pytest: Add a test for the `sendonion` method
cdecker Nov 8, 2019
fa3bc38
pay: Handle payment failures resulting from sendonion correctly
cdecker Nov 8, 2019
951b9fa
json: Add helper to extract a secret from JSON
cdecker Nov 8, 2019
006e7bc
pay: Make `erring_node` optional in `struct routing_failure`
cdecker Nov 11, 2019
d5dd834
pay: Make `erring_channel` optional in `struct routing_failure`
cdecker Nov 11, 2019
85f86f7
pay: Allow `sendonion` callers to provide `shared_secrets`
cdecker Nov 11, 2019
92b60bc
cleanup: The failchannel is not tal-allocated when first assigned
cdecker Nov 11, 2019
c04d415
json-rpc: Add the error onion if we stored it in the DB
cdecker Nov 11, 2019
a95abb9
pay: Allow payments initiated with `sendonion` to be retried
cdecker Nov 12, 2019
28451ca
doc: Add manpages for `createonion` and `sendonion`
cdecker Nov 14, 2019
26255e7
sphinx: Make the sphinx_hop struct public
cdecker Nov 24, 2019
1286a00
param: Encapsulate hops parsing in a param_hops_array helper
cdecker Nov 24, 2019
0109fd7
json-rpc: Rename onion payload type to style
cdecker Nov 25, 2019
65342b1
json-rpc: Add helper for an array of secrets
cdecker Nov 25, 2019
f256bd8
json-rpc: Only show the amount_msat field if we know it in payments
cdecker Nov 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions common/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b)
return false;
}

bool json_to_secret(const char *buffer, const jsmntok_t *tok, struct secret *dest)
{
return hex_decode(buffer + tok->start, tok->end - tok->start,
dest->data, sizeof(struct secret));
}

u8 *json_tok_bin_from_hex(const tal_t *ctx, const char *buffer, const jsmntok_t *tok)
{
u8 *result;
Expand Down
4 changes: 4 additions & 0 deletions common/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define LIGHTNING_COMMON_JSON_H
#include "config.h"
#include <bitcoin/preimage.h>
#include <bitcoin/privkey.h>
#include <ccan/tal/tal.h>
#include <stdbool.h>
#include <stdint.h>
Expand Down Expand Up @@ -49,6 +50,9 @@ bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num);
/* Extract boolean from this */
bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b);

/* Extract a secret from this. */
bool json_to_secret(const char *buffer, const jsmntok_t *tok, struct secret *dest);

/* Is this a number? [0..9]+ */
bool json_tok_is_num(const char *buffer, const jsmntok_t *tok);

Expand Down
127 changes: 127 additions & 0 deletions common/json_tok.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,130 @@ struct command_result *param_channel_id(struct command *cmd, const char *name,
name, json_tok_full_len(tok),
json_tok_full(buffer, tok));
}

struct command_result *param_secret(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
struct secret **secret)
{
*secret = tal(cmd, struct secret);
if (hex_decode(buffer + tok->start,
tok->end - tok->start,
*secret, sizeof(**secret)))
return NULL;

return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a 32 byte hex value, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
}

struct command_result *param_bin_from_hex(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
u8 **bin)
{
*bin = json_tok_bin_from_hex(cmd, buffer, tok);
if (bin != NULL)
return NULL;
else
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a hex value, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
}

struct command_result *param_hops_array(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
struct sphinx_hop **hops)
{
const jsmntok_t *hop, *payloadtok, *styletok, *pubkeytok;
struct sphinx_hop h;
size_t i;
if (tok->type != JSMN_ARRAY) {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be an array of hops, got '%.*s'", name,
tok->end - tok->start, buffer + tok->start);
}

*hops = tal_arr(cmd, struct sphinx_hop, 0);

json_for_each_arr(i, hop, tok) {

payloadtok = json_get_member(buffer, hop, "payload");
styletok = json_get_member(buffer, hop, "style");
pubkeytok = json_get_member(buffer, hop, "pubkey");

if (!pubkeytok)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Hop %zu does not have a pubkey", i);

if (!payloadtok)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Hop %zu does not have a payload", i);

h.payload = json_tok_bin_from_hex(*hops, buffer, payloadtok);
if (!json_to_pubkey(buffer, pubkeytok, &h.pubkey))
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"'pubkey' should be a pubkey, not '%.*s'",
pubkeytok->end - pubkeytok->start,
buffer + pubkeytok->start);

if (!h.payload)
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"'payload' should be a hex encoded binary, not '%.*s'",
pubkeytok->end - pubkeytok->start,
buffer + pubkeytok->start);

if (!styletok || json_tok_streq(buffer, styletok, "tlv")) {
h.type = SPHINX_TLV_PAYLOAD;
} else if (json_tok_streq(buffer, styletok, "legacy")) {
h.type = SPHINX_V0_PAYLOAD;
} else {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"Unknown payload type for hop %zu: '%.*s'", i,
pubkeytok->end - pubkeytok->start,
buffer + pubkeytok->start);
}

tal_arr_expand(hops, h);
}

if (tal_count(*hops) == 0) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"At least one hop must be specified.");
}

return NULL;
}

struct command_result *param_secrets_array(struct command *cmd,
const char *name, const char *buffer,
const jsmntok_t *tok,
struct secret **secrets)
{
size_t i;
const jsmntok_t *s;
struct secret secret;

if (tok->type != JSMN_ARRAY) {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be an array of secrets, got '%.*s'", name,
tok->end - tok->start, buffer + tok->start);
}

*secrets = tal_arr(cmd, struct secret, 0);
json_for_each_arr(i, s, tok) {
if (!hex_decode(buffer + s->start, s->end - s->start, &secret,
sizeof(secret)))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s[%zu]' should be a 32 byte hex "
"value, not '%.*s'",
name, i, s->end - s->start,
buffer + s->start);

tal_arr_expand(secrets, secret);
}
return NULL;
}
20 changes: 20 additions & 0 deletions common/json_tok.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <ccan/short_types/short_types.h>
#include <common/json.h>
#include <common/node_id.h>
#include <common/sphinx.h>
#include <wire/wire.h>

struct amount_msat;
Expand Down Expand Up @@ -111,4 +112,23 @@ struct command_result *param_ignore(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
const void *unused);

/* Extract a secret from this string */
struct command_result *param_secret(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
struct secret **secret);

/* Extract a binary value from the param and unhexlify it. */
struct command_result *param_bin_from_hex(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
u8 **bin);

struct command_result *param_hops_array(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
struct sphinx_hop **hops);

struct command_result *param_secrets_array(struct command *cmd,
const char *name, const char *buffer,
const jsmntok_t *tok,
struct secret **secrets);

#endif /* LIGHTNING_COMMON_JSON_TOK_H */
16 changes: 0 additions & 16 deletions common/sphinx.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,6 @@ struct keyset {
u8 gamma[KEY_LEN];
};

/*
* All the necessary information to generate a valid onion for this hop on a
* sphinx path. The payload is preserialized in order since the onion
* generation is payload agnostic. */
struct sphinx_hop {
struct pubkey pubkey;
enum sphinx_payload_type type;
const u8 *payload;
u8 hmac[HMAC_SIZE];
};

/* Encapsulates the information about a given payment path for the the onion
* routing algorithm.
*/
Expand Down Expand Up @@ -542,11 +531,6 @@ static bool sphinx_write_frame(u8 *dest, const struct sphinx_hop *hop)
size_t padding_size;
int pos = 0;

#if !EXPERIMENTAL_FEATURES
if (hop->type != SPHINX_V0_PAYLOAD)
return false;
#endif

Comment on lines -545 to -549
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changelog should actually announce TLV! (Changelog-Added: We handle modern TLV-style payloads). This is important because we never announce EXPERIMENTAL features.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping you'd add a changelog entry to #3167, since you did all the heavy lifting there, but you're right, that we don't note experimental features in the changelog.

I'd propose the following ling:

Changelog-Added: Plugins may now handle modern TLV-style payloads via the `htlc_accepted` hook

That's a less broad claim than full TLV support for sending and receiving without a plugin, which as you noted is still experimental.

/* Backwards compatibility for the legacy hop_data format. */
if (hop->type == SPHINX_V0_PAYLOAD)
dest[pos++] = 0x00;
Expand Down
11 changes: 11 additions & 0 deletions common/sphinx.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ enum sphinx_payload_type {
SPHINX_RAW_PAYLOAD = 255,
};

/*
* All the necessary information to generate a valid onion for this hop on a
* sphinx path. The payload is preserialized in order since the onion
* generation is payload agnostic. */
struct sphinx_hop {
struct pubkey pubkey;
enum sphinx_payload_type type;
const u8 *payload;
u8 hmac[HMAC_SIZE];
};

struct route_step {
enum route_next_case nextcase;
struct onionpacket *next;
Expand Down
4 changes: 4 additions & 0 deletions common/test/run-param.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEED
bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct node_id *id UNNEEDED)
{ fprintf(stderr, "json_to_node_id called!\n"); abort(); }
/* Generated stub for json_to_pubkey */
bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
struct pubkey *pubkey UNNEEDED)
{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

/* We do this lightningd-style: */
Expand Down
2 changes: 2 additions & 0 deletions doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ MANPAGES := doc/lightning-cli.1 \
doc/lightning-checkmessage.7 \
doc/lightning-close.7 \
doc/lightning-connect.7 \
doc/lightning-createonion.7 \
doc/lightning-decodepay.7 \
doc/lightning-delexpiredinvoice.7 \
doc/lightning-delinvoice.7 \
Expand All @@ -32,6 +33,7 @@ MANPAGES := doc/lightning-cli.1 \
doc/lightning-newaddr.7 \
doc/lightning-pay.7 \
doc/lightning-plugin.7 \
doc/lightning-sendonion.7 \
doc/lightning-sendpay.7 \
doc/lightning-setchannelfee.7 \
doc/lightning-signmessage.7 \
Expand Down
2 changes: 2 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ c-lightning Documentation
lightning-cli <lightning-cli.1.md>
lightning-close <lightning-close.7.md>
lightning-connect <lightning-connect.7.md>
lightning-createonion <lightning-createonion.7.md>
lightning-decodepay <lightning-decodepay.7.md>
lightning-delexpiredinvoice <lightning-delexpiredinvoice.7.md>
lightning-delinvoice <lightning-delinvoice.7.md>
Expand All @@ -55,6 +56,7 @@ c-lightning Documentation
lightning-newaddr <lightning-newaddr.7.md>
lightning-pay <lightning-pay.7.md>
lightning-plugin <lightning-plugin.7.md>
lightning-sendonion <lightning-sendonion.7.md>
lightning-sendpay <lightning-sendpay.7.md>
lightning-setchannelfee <lightning-setchannelfee.7.md>
lightning-signmessage <lightning-signmessage.7.md>
Expand Down
Loading