mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
839909d2cf
Thanks to @t-bast, who made this possible by interop testing with Eclair! Changelog-Added: Protocol: can now send and receive TLV-style onion messages. Changelog-Added: Protocol: can now send and receive BOLT11 payment_secrets. Changelog-Added: Protocol: can now receive basic multi-part payments. Changelog-Added: RPC: low-level commands sendpay and waitsendpay can now be used to manually send multi-part payments. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
256 lines
6.9 KiB
C
256 lines
6.9 KiB
C
#include "features.h"
|
|
#include <assert.h>
|
|
#include <ccan/array_size/array_size.h>
|
|
#include <common/utils.h>
|
|
#include <wire/peer_wire.h>
|
|
|
|
static const u32 our_features[] = {
|
|
OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT),
|
|
OPTIONAL_FEATURE(OPT_UPFRONT_SHUTDOWN_SCRIPT),
|
|
OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES),
|
|
OPTIONAL_FEATURE(OPT_VAR_ONION),
|
|
OPTIONAL_FEATURE(OPT_PAYMENT_SECRET),
|
|
OPTIONAL_FEATURE(OPT_BASIC_MPP),
|
|
OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX),
|
|
OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY),
|
|
};
|
|
|
|
enum feature_copy_style {
|
|
/* Feature is not exposed (importantly, being 0, this is the default!). */
|
|
FEATURE_DONT_REPRESENT,
|
|
/* Feature is exposed. */
|
|
FEATURE_REPRESENT,
|
|
/* Feature is exposed, but always optional. */
|
|
FEATURE_REPRESENT_AS_OPTIONAL,
|
|
};
|
|
|
|
enum feature_place {
|
|
INIT_FEATURE,
|
|
GLOBAL_INIT_FEATURE,
|
|
NODE_ANNOUNCE_FEATURE,
|
|
BOLT11_FEATURE,
|
|
};
|
|
#define NUM_FEATURE_PLACE (BOLT11_FEATURE+1)
|
|
|
|
struct feature_style {
|
|
u32 bit;
|
|
enum feature_copy_style copy_style[NUM_FEATURE_PLACE];
|
|
};
|
|
|
|
static const struct feature_style feature_styles[] = {
|
|
{ OPT_DATA_LOSS_PROTECT,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_INITIAL_ROUTING_SYNC,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT_AS_OPTIONAL,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_DONT_REPRESENT } },
|
|
{ OPT_UPFRONT_SHUTDOWN_SCRIPT,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_GOSSIP_QUERIES,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_GOSSIP_QUERIES_EX,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_VAR_ONION,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[GLOBAL_INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
|
|
[BOLT11_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_STATIC_REMOTEKEY,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[GLOBAL_INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_PAYMENT_SECRET,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
|
|
[BOLT11_FEATURE] = FEATURE_REPRESENT } },
|
|
{ OPT_BASIC_MPP,
|
|
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
|
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
|
|
[BOLT11_FEATURE] = FEATURE_REPRESENT } },
|
|
};
|
|
|
|
static enum feature_copy_style feature_copy_style(u32 f, enum feature_place p)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_SIZE(feature_styles); i++) {
|
|
if (feature_styles[i].bit == COMPULSORY_FEATURE(f))
|
|
return feature_styles[i].copy_style[p];
|
|
}
|
|
abort();
|
|
}
|
|
|
|
/* BOLT #1:
|
|
*
|
|
* All data fields are unsigned big-endian unless otherwise specified.
|
|
*/
|
|
void set_feature_bit(u8 **ptr, u32 bit)
|
|
{
|
|
size_t len = tal_count(*ptr);
|
|
if (bit / 8 >= len) {
|
|
size_t newlen = (bit / 8) + 1;
|
|
u8 *newarr = tal_arrz(tal_parent(*ptr), u8, newlen);
|
|
memcpy(newarr + (newlen - len), *ptr, len);
|
|
tal_free(*ptr);
|
|
*ptr = newarr;
|
|
len = newlen;
|
|
}
|
|
(*ptr)[len - 1 - bit / 8] |= (1 << (bit % 8));
|
|
}
|
|
|
|
static bool test_bit(const u8 *features, size_t byte, unsigned int bit)
|
|
{
|
|
assert(byte < tal_count(features));
|
|
return features[tal_count(features) - 1 - byte] & (1 << (bit % 8));
|
|
}
|
|
|
|
static u8 *mkfeatures(const tal_t *ctx, enum feature_place place)
|
|
{
|
|
u8 *f = tal_arr(ctx, u8, 0);
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(our_features); i++) {
|
|
switch (feature_copy_style(our_features[i], place)) {
|
|
case FEATURE_DONT_REPRESENT:
|
|
continue;
|
|
case FEATURE_REPRESENT:
|
|
set_feature_bit(&f, our_features[i]);
|
|
continue;
|
|
case FEATURE_REPRESENT_AS_OPTIONAL:
|
|
set_feature_bit(&f, OPTIONAL_FEATURE(our_features[i]));
|
|
continue;
|
|
}
|
|
abort();
|
|
}
|
|
return f;
|
|
}
|
|
|
|
u8 *get_offered_nodefeatures(const tal_t *ctx)
|
|
{
|
|
return mkfeatures(ctx, NODE_ANNOUNCE_FEATURE);
|
|
}
|
|
|
|
u8 *get_offered_initfeatures(const tal_t *ctx)
|
|
{
|
|
return mkfeatures(ctx, INIT_FEATURE);
|
|
}
|
|
|
|
u8 *get_offered_globalinitfeatures(const tal_t *ctx)
|
|
{
|
|
return mkfeatures(ctx, GLOBAL_INIT_FEATURE);
|
|
}
|
|
|
|
u8 *get_offered_bolt11features(const tal_t *ctx)
|
|
{
|
|
return mkfeatures(ctx, BOLT11_FEATURE);
|
|
}
|
|
|
|
bool feature_is_set(const u8 *features, size_t bit)
|
|
{
|
|
size_t bytenum = bit / 8;
|
|
|
|
if (bytenum >= tal_count(features))
|
|
return false;
|
|
|
|
return test_bit(features, bytenum, bit % 8);
|
|
}
|
|
|
|
bool feature_offered(const u8 *features, size_t f)
|
|
{
|
|
return feature_is_set(features, COMPULSORY_FEATURE(f))
|
|
|| feature_is_set(features, OPTIONAL_FEATURE(f));
|
|
}
|
|
|
|
static bool feature_supported(int feature_bit,
|
|
const u32 *supported,
|
|
size_t num_supported)
|
|
{
|
|
for (size_t i = 0; i < num_supported; i++) {
|
|
if (OPTIONAL_FEATURE(supported[i])
|
|
== OPTIONAL_FEATURE(feature_bit))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool feature_negotiated(const u8 *lfeatures, size_t f)
|
|
{
|
|
if (!feature_offered(lfeatures, f))
|
|
return false;
|
|
return feature_supported(f, our_features, ARRAY_SIZE(our_features));
|
|
}
|
|
|
|
/**
|
|
* all_supported_features - Check if we support what's being asked
|
|
*
|
|
* Given the features vector that the remote connection is expecting
|
|
* from us, we check to see if we support all even bit features, i.e.,
|
|
* the required features.
|
|
*
|
|
* @bitmap: the features bitmap the peer is asking for
|
|
* @supported: array of features we support
|
|
* @num_supported: how many elements in supported
|
|
*
|
|
* Returns -1 on success, or first unsupported feature.
|
|
*/
|
|
static int all_supported_features(const u8 *bitmap,
|
|
const u32 *supported,
|
|
size_t num_supported)
|
|
{
|
|
size_t len = tal_count(bitmap) * 8;
|
|
|
|
/* It's OK to be odd: only check even bits. */
|
|
for (size_t bitnum = 0; bitnum < len; bitnum += 2) {
|
|
if (!test_bit(bitmap, bitnum/8, bitnum%8))
|
|
continue;
|
|
|
|
if (feature_supported(bitnum, supported, num_supported))
|
|
continue;
|
|
|
|
return bitnum;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int features_unsupported(const u8 *features)
|
|
{
|
|
/* BIT 2 would logically be "compulsory initial_routing_sync", but
|
|
* that does not exist, so we special case it. */
|
|
if (feature_is_set(features,
|
|
COMPULSORY_FEATURE(OPT_INITIAL_ROUTING_SYNC)))
|
|
return COMPULSORY_FEATURE(OPT_INITIAL_ROUTING_SYNC);
|
|
|
|
return all_supported_features(features,
|
|
our_features,
|
|
ARRAY_SIZE(our_features));
|
|
}
|
|
|
|
static const char *feature_name(const tal_t *ctx, size_t f)
|
|
{
|
|
static const char *fnames[] = {
|
|
"option_data_loss_protect",
|
|
"option_initial_routing_sync",
|
|
"option_upfront_shutdown_script",
|
|
"option_gossip_queries",
|
|
"option_var_onion_optin",
|
|
"option_gossip_queries_ex",
|
|
"option_static_remotekey",
|
|
"option_payment_secret",
|
|
"option_basic_mpp",
|
|
};
|
|
|
|
assert(f / 2 < ARRAY_SIZE(fnames));
|
|
return tal_fmt(ctx, "%s/%s",
|
|
fnames[f / 2], (f & 1) ? "odd" : "even");
|
|
}
|
|
|
|
const char **list_supported_features(const tal_t *ctx)
|
|
{
|
|
const char **list = tal_arr(ctx, const char *, 0);
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(our_features); i++)
|
|
tal_arr_expand(&list, feature_name(list, our_features[i]));
|
|
|
|
return list;
|
|
}
|