common/features: use bitmaps internally, have explicit init function.

This is to prepare for dynamic features, including making plugins first
class citizens at setting them.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2020-03-31 09:09:00 +10:30
committed by Christian Decker
parent c59327dce1
commit afb76392e4
11 changed files with 180 additions and 76 deletions

View File

@@ -1,19 +1,39 @@
#include "features.h"
#include <assert.h>
#include <ccan/array_size/array_size.h>
#include <common/memleak.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),
};
/* We keep a map of our features for each context, with the assumption that
* the init features is a superset of the others. */
static struct feature_set *our_features;
/* FIXME: Remove once all subdaemons call features_init() */
static const u8 *our_feature_bits(enum feature_place place)
{
if (!our_features) {
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),
};
u8 *f = tal_arr(NULL, u8, 0);
for (size_t i = 0; i < ARRAY_SIZE(our_features); i++)
set_feature_bit(&f, our_features[i]);
features_core_init(take(f));
}
/* This is currently always a superset of other bits */
return our_features->bits[place];
}
enum feature_copy_style {
/* Feature is not exposed (importantly, being 0, this is the default!). */
@@ -24,15 +44,6 @@ enum feature_copy_style {
FEATURE_REPRESENT_AS_OPTIONAL,
};
enum feature_place {
INIT_FEATURE,
GLOBAL_INIT_FEATURE,
NODE_ANNOUNCE_FEATURE,
CHANNEL_FEATURE,
BOLT11_FEATURE,
};
#define NUM_FEATURE_PLACE (BOLT11_FEATURE+1)
struct feature_style {
u32 bit;
enum feature_copy_style copy_style[NUM_FEATURE_PLACE];
@@ -82,6 +93,51 @@ static enum feature_copy_style feature_copy_style(u32 f, enum feature_place p)
abort();
}
static u8 *mkfeatures(const tal_t *ctx, enum feature_place place)
{
u8 *f = tal_arr(ctx, u8, 0);
const u8 *base = our_features->bits[INIT_FEATURE];
assert(place != INIT_FEATURE);
for (size_t i = 0; i < tal_bytelen(base)*8; i++) {
if (!feature_is_set(base, i))
continue;
switch (feature_copy_style(i, place)) {
case FEATURE_DONT_REPRESENT:
continue;
case FEATURE_REPRESENT:
set_feature_bit(&f, i);
continue;
case FEATURE_REPRESENT_AS_OPTIONAL:
set_feature_bit(&f, OPTIONAL_FEATURE(i));
continue;
}
abort();
}
return f;
}
struct feature_set *features_core_init(const u8 *feature_bits)
{
assert(!our_features);
our_features = notleak(tal(NULL, struct feature_set));
our_features->bits[INIT_FEATURE]
= tal_dup_talarr(our_features, u8, feature_bits);
/* Make other masks too */
for (enum feature_place f = INIT_FEATURE+1; f < NUM_FEATURE_PLACE; f++)
our_features->bits[f] = mkfeatures(our_features, f);
return our_features;
}
void features_cleanup(void)
{
our_features = tal_free(our_features);
}
/* BOLT #1:
*
* All data fields are unsigned big-endian unless otherwise specified.
@@ -106,39 +162,19 @@ static bool test_bit(const u8 *features, size_t byte, unsigned int bit)
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);
return tal_dup_talarr(ctx, u8, our_feature_bits(NODE_ANNOUNCE_FEATURE));
}
u8 *get_offered_initfeatures(const tal_t *ctx)
{
return mkfeatures(ctx, INIT_FEATURE);
return tal_dup_talarr(ctx, u8, our_feature_bits(INIT_FEATURE));
}
u8 *get_offered_globalinitfeatures(const tal_t *ctx)
{
return mkfeatures(ctx, GLOBAL_INIT_FEATURE);
return tal_dup_talarr(ctx, u8, our_feature_bits(GLOBAL_INIT_FEATURE));
}
static void clear_feature_bit(u8 *features, u32 bit)
@@ -159,7 +195,7 @@ static void clear_feature_bit(u8 *features, u32 bit)
*/
u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures)
{
u8 *f = mkfeatures(ctx, CHANNEL_FEATURE);
u8 *f = tal_dup_talarr(ctx, u8, our_feature_bits(CHANNEL_FEATURE));
size_t max_len = 0;
/* Clear any features which they didn't offer too */
@@ -181,7 +217,7 @@ u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures)
u8 *get_offered_bolt11features(const tal_t *ctx)
{
return mkfeatures(ctx, BOLT11_FEATURE);
return tal_dup_talarr(ctx, u8, our_feature_bits(BOLT11_FEATURE));
}
bool feature_is_set(const u8 *features, size_t bit)
@@ -200,23 +236,10 @@ bool feature_offered(const u8 *features, size_t 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));
return feature_offered(lfeatures, f)
&& feature_offered(our_feature_bits(INIT_FEATURE), f);
}
/**
@@ -227,14 +250,10 @@ bool feature_negotiated(const u8 *lfeatures, size_t f)
* 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)
static int all_supported_features(const u8 *bitmap)
{
size_t len = tal_count(bitmap) * 8;
@@ -243,7 +262,7 @@ static int all_supported_features(const u8 *bitmap,
if (!test_bit(bitmap, bitnum/8, bitnum%8))
continue;
if (feature_supported(bitnum, supported, num_supported))
if (feature_offered(our_feature_bits(INIT_FEATURE), bitnum))
continue;
return bitnum;
@@ -259,9 +278,7 @@ int features_unsupported(const u8 *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));
return all_supported_features(features);
}
static const char *feature_name(const tal_t *ctx, size_t f)
@@ -278,7 +295,10 @@ static const char *feature_name(const tal_t *ctx, size_t f)
"option_basic_mpp",
};
assert(f / 2 < ARRAY_SIZE(fnames));
if (f / 2 >= ARRAY_SIZE(fnames))
return tal_fmt(ctx, "option_unknown_%zu/%s",
COMPULSORY_FEATURE(f), (f & 1) ? "odd" : "even");
return tal_fmt(ctx, "%s/%s",
fnames[f / 2], (f & 1) ? "odd" : "even");
}
@@ -287,8 +307,10 @@ 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]));
for (size_t i = 0; i < tal_bytelen(our_feature_bits(INIT_FEATURE)) * 8; i++) {
if (test_bit(our_feature_bits(INIT_FEATURE), i / 8, i % 8))
tal_arr_expand(&list, feature_name(list, i));
}
return list;
}