diff --git a/channeld/channel_wire.csv b/channeld/channel_wire.csv index 6aed51038..20e270661 100644 --- a/channeld/channel_wire.csv +++ b/channeld/channel_wire.csv @@ -1,12 +1,14 @@ #include #include #include +#include #include #include # Begin! (passes gossipd-client fd) msgtype,channel_init,1000 msgdata,channel_init,chainparams,chainparams, +msgdata,channel_init,feature_set,feature_set, msgdata,channel_init,funding_txid,bitcoin_txid, msgdata,channel_init,funding_txout,u16, msgdata,channel_init,funding_satoshi,amount_sat, diff --git a/channeld/channeld.c b/channeld/channeld.c index 63a9ae599..c30786a41 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2753,6 +2753,7 @@ static void init_channel(struct peer *peer) secp256k1_ecdsa_signature *remote_ann_node_sig; secp256k1_ecdsa_signature *remote_ann_bitcoin_sig; bool option_static_remotekey; + struct feature_set *feature_set; #if !DEVELOPER bool dev_fail_process_onionpacket; /* Ignored */ #endif @@ -2764,6 +2765,7 @@ static void init_channel(struct peer *peer) msg = wire_sync_read(tmpctx, MASTER_FD); if (!fromwire_channel_init(peer, msg, &chainparams, + &feature_set, &funding_txid, &funding_txout, &funding, &minimum_depth, @@ -2818,6 +2820,10 @@ static void init_channel(struct peer *peer) &dev_fail_process_onionpacket)) { master_badmsg(WIRE_CHANNEL_INIT, msg); } + + /* Now we know what features to advertize. */ + features_init(take(feature_set)); + /* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = HSM */ per_peer_state_set_fds(peer->pps, 3, 4, 5); diff --git a/closingd/Makefile b/closingd/Makefile index 847d8ef4c..7b3877c7f 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -54,7 +54,6 @@ CLOSINGD_COMMON_OBJS := \ common/daemon_conn.o \ common/dev_disconnect.o \ common/derive_basepoints.o \ - common/features.o \ common/gen_peer_status_wire.o \ common/gen_status_wire.o \ common/gossip_rcvd_filter.o \ diff --git a/common/features.c b/common/features.c index 2fe4500cc..066209fc5 100644 --- a/common/features.c +++ b/common/features.c @@ -9,32 +9,6 @@ * 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!). */ FEATURE_DONT_REPRESENT, @@ -133,6 +107,20 @@ struct feature_set *features_core_init(const u8 *feature_bits) return our_features; } +void features_init(struct feature_set *fset TAKES) +{ + assert(!our_features); + + if (taken(fset)) + our_features = notleak(tal_steal(NULL, fset)); + else { + our_features = notleak(tal(NULL, struct feature_set)); + for (size_t i = 0; i < ARRAY_SIZE(fset->bits); i++) + our_features->bits[i] = tal_dup_talarr(our_features, u8, + fset->bits[i]); + } +} + void features_cleanup(void) { our_features = tal_free(our_features); @@ -164,17 +152,17 @@ static bool test_bit(const u8 *features, size_t byte, unsigned int bit) u8 *get_offered_nodefeatures(const tal_t *ctx) { - return tal_dup_talarr(ctx, u8, our_feature_bits(NODE_ANNOUNCE_FEATURE)); + return tal_dup_talarr(ctx, u8, our_features->bits[NODE_ANNOUNCE_FEATURE]); } u8 *get_offered_initfeatures(const tal_t *ctx) { - return tal_dup_talarr(ctx, u8, our_feature_bits(INIT_FEATURE)); + return tal_dup_talarr(ctx, u8, our_features->bits[INIT_FEATURE]); } u8 *get_offered_globalinitfeatures(const tal_t *ctx) { - return tal_dup_talarr(ctx, u8, our_feature_bits(GLOBAL_INIT_FEATURE)); + return tal_dup_talarr(ctx, u8, our_features->bits[GLOBAL_INIT_FEATURE]); } static void clear_feature_bit(u8 *features, u32 bit) @@ -195,7 +183,7 @@ static void clear_feature_bit(u8 *features, u32 bit) */ u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures) { - u8 *f = tal_dup_talarr(ctx, u8, our_feature_bits(CHANNEL_FEATURE)); + u8 *f = tal_dup_talarr(ctx, u8, our_features->bits[CHANNEL_FEATURE]); size_t max_len = 0; /* Clear any features which they didn't offer too */ @@ -217,7 +205,7 @@ u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures) u8 *get_offered_bolt11features(const tal_t *ctx) { - return tal_dup_talarr(ctx, u8, our_feature_bits(BOLT11_FEATURE)); + return tal_dup_talarr(ctx, u8, our_features->bits[BOLT11_FEATURE]); } bool feature_is_set(const u8 *features, size_t bit) @@ -239,7 +227,7 @@ bool feature_offered(const u8 *features, size_t f) bool feature_negotiated(const u8 *lfeatures, size_t f) { return feature_offered(lfeatures, f) - && feature_offered(our_feature_bits(INIT_FEATURE), f); + && feature_offered(our_features->bits[INIT_FEATURE], f); } /** @@ -262,7 +250,7 @@ static int all_supported_features(const u8 *bitmap) if (!test_bit(bitmap, bitnum/8, bitnum%8)) continue; - if (feature_offered(our_feature_bits(INIT_FEATURE), bitnum)) + if (feature_offered(our_features->bits[INIT_FEATURE], bitnum)) continue; return bitnum; @@ -307,8 +295,8 @@ const char **list_supported_features(const tal_t *ctx) { const char **list = tal_arr(ctx, const char *, 0); - 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)) + for (size_t i = 0; i < tal_bytelen(our_features->bits[INIT_FEATURE]) * 8; i++) { + if (test_bit(our_features->bits[INIT_FEATURE], i / 8, i % 8)) tal_arr_expand(&list, feature_name(list, i)); } @@ -338,3 +326,24 @@ u8 *featurebits_or(const tal_t *ctx, const u8 *f1 TAKES, const u8 *f2 TAKES) return result; } +struct feature_set *fromwire_feature_set(const tal_t *ctx, + const u8 **cursor, size_t *max) +{ + struct feature_set *fset = tal(ctx, struct feature_set); + + for (size_t i = 0; i < ARRAY_SIZE(fset->bits); i++) + fset->bits[i] = fromwire_tal_arrn(fset, cursor, max, + fromwire_u16(cursor, max)); + + if (!*cursor) + return tal_free(fset); + return fset; +} + +void towire_feature_set(u8 **pptr, const struct feature_set *fset) +{ + for (size_t i = 0; i < ARRAY_SIZE(fset->bits); i++) { + towire_u16(pptr, tal_bytelen(fset->bits[i])); + towire_u8_array(pptr, fset->bits[i], tal_bytelen(fset->bits[i])); + } +} diff --git a/common/features.h b/common/features.h index 7ef68baa4..feb67a9d6 100644 --- a/common/features.h +++ b/common/features.h @@ -21,9 +21,16 @@ struct feature_set { /* Initialize core features (for lightningd). */ struct feature_set *features_core_init(const u8 *features TAKES); +/* Initialize subdaemon features. */ +void features_init(struct feature_set *fset TAKES); + /* Free feature allocations */ void features_cleanup(void); +struct feature_set *fromwire_feature_set(const tal_t *ctx, + const u8 **ptr, size_t *max); +void towire_feature_set(u8 **pptr, const struct feature_set *fset); + /* Returns -1 if we're OK with all these offered features, otherwise first * unsupported (even) feature. */ int features_unsupported(const u8 *features); diff --git a/common/test/run-features.c b/common/test/run-features.c index 1666bc694..f75f96804 100644 --- a/common/test/run-features.c +++ b/common/test/run-features.c @@ -30,6 +30,19 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) /* Generated stub for fromwire_fail */ const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ diff --git a/connectd/connect_wire.csv b/connectd/connect_wire.csv index cac7cdb4d..8a0cf0b98 100644 --- a/connectd/connect_wire.csv +++ b/connectd/connect_wire.csv @@ -1,10 +1,12 @@ #include +#include #include #include #include msgtype,connectctl_init,2000 msgdata,connectctl_init,chainparams,chainparams, +msgdata,connectctl_init,feature_set,feature_set, msgdata,connectctl_init,id,node_id, msgdata,connectctl_init,num_wireaddrs,u16, msgdata,connectctl_init,wireaddrs,wireaddr_internal,num_wireaddrs diff --git a/connectd/connectd.c b/connectd/connectd.c index cc0da37da..d9a1f94f8 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1188,12 +1188,14 @@ static struct io_plan *connect_init(struct io_conn *conn, struct wireaddr_internal *proposed_wireaddr; enum addr_listen_announce *proposed_listen_announce; struct wireaddr *announcable; + struct feature_set *feature_set; char *tor_password; /* Fields which require allocation are allocated off daemon */ if (!fromwire_connectctl_init( daemon, msg, &chainparams, + &feature_set, &daemon->id, &proposed_wireaddr, &proposed_listen_announce, @@ -1206,6 +1208,9 @@ static struct io_plan *connect_init(struct io_conn *conn, master_badmsg(WIRE_CONNECTCTL_INIT, msg); } + /* Now we know what features to advertize. */ + features_init(take(feature_set)); + if (!pubkey_from_node_id(&daemon->mykey, &daemon->id)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Invalid id for me %s", diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index 35f7f32d1..96e102b50 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -33,9 +33,22 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) /* Generated stub for fromwire_fail */ const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* No randomness please, we want to replicate test vectors. */ diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index 0c377d744..5a6977140 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -33,9 +33,22 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) /* Generated stub for fromwire_fail */ const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* No randomness please, we want to replicate test vectors. */ diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 2a393fc42..a9875bcad 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -1,10 +1,12 @@ #include +#include #include #include # Initialize the gossip daemon. msgtype,gossipctl_init,3000 msgdata,gossipctl_init,chainparams,chainparams, +msgdata,gossipctl_init,feature_set,feature_set, msgdata,gossipctl_init,id,node_id, msgdata,gossipctl_init,gflen,u16, msgdata,gossipctl_init,globalfeatures,u8,gflen diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 88b6f80da..da3612a10 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -828,9 +828,11 @@ static struct io_plan *gossip_init(struct io_conn *conn, u32 *dev_gossip_time; bool dev_fast_gossip, dev_fast_gossip_prune; u32 timestamp; + struct feature_set *feature_set; if (!fromwire_gossipctl_init(daemon, msg, &chainparams, + &feature_set, &daemon->id, &daemon->nodefeatures, daemon->rgb, @@ -842,6 +844,7 @@ static struct io_plan *gossip_init(struct io_conn *conn, master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } + features_init(take(feature_set)); daemon->rstate = new_routing_state(daemon, &daemon->id, &daemon->peers, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 890b3396b..aeac9f14a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -459,6 +459,7 @@ void peer_start_channeld(struct channel *channel, initmsg = towire_channel_init(tmpctx, chainparams, + ld->feature_set, &channel->funding_txid, channel->funding_outnum, channel->funding, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 066003914..8bdea18ec 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -370,6 +370,7 @@ int connectd_init(struct lightningd *ld) msg = towire_connectctl_init( tmpctx, chainparams, + ld->feature_set, &ld->id, wireaddrs, listen_announce, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 58d2ee347..cbdcec696 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -214,6 +214,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd) msg = towire_gossipctl_init( tmpctx, chainparams, + ld->feature_set, &ld->id, node_featurebits, ld->rgb, diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index a2dfc5540..ab280eff4 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -119,6 +119,9 @@ struct lightningd { /* This is us. */ struct node_id id; + /* Feature set we offer. */ + const struct feature_set *feature_set; + /* My name is... my favorite color is... */ u8 *alias; /* At least 32 bytes (zero-filled) */ u8 *rgb; /* tal_len() == 3. */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 0bf4f8af7..c020fb423 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -999,6 +999,7 @@ void peer_start_openingd(struct peer *peer, msg = towire_opening_init(NULL, chainparams, + peer->ld->feature_set, &uc->our_config, max_to_self_delay, min_effective_htlc_capacity, diff --git a/lightningd/options.c b/lightningd/options.c index 6af714dee..5be2fed72 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1004,7 +1004,7 @@ void setup_color_and_alias(struct lightningd *ld) } } -static void setup_default_features(void) +static struct feature_set *setup_default_features(void) { static const u32 default_features[] = { OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT), @@ -1021,7 +1021,7 @@ static void setup_default_features(void) for (size_t i = 0; i < ARRAY_SIZE(default_features); i++) set_feature_bit(&f, default_features[i]); - features_core_init(take(f)); + return features_core_init(take(f)); } void handle_early_opts(struct lightningd *ld, int argc, char *argv[]) @@ -1030,7 +1030,7 @@ void handle_early_opts(struct lightningd *ld, int argc, char *argv[]) setup_option_allocators(); /* Make sure options are populated. */ - setup_default_features(); + ld->feature_set = setup_default_features(); /*~ List features immediately, before doing anything interesting */ opt_register_early_noarg("--list-features-only", diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 00cb83e8a..73a760bd1 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -54,6 +54,13 @@ bool fromwire_onchain_spent(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, s /* Generated stub for fromwire_peektype */ int fromwire_peektype(const u8 *cursor UNNEEDED) { fprintf(stderr, "fromwire_peektype called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for htlc_offered_wscript */ u8 *htlc_offered_wscript(const tal_t *ctx UNNEEDED, const struct ripemd160 *ripemd UNNEEDED, @@ -164,6 +171,12 @@ u8 *towire_onchain_missing_htlc_output(const tal_t *ctx UNNEEDED, const struct h /* Generated stub for towire_onchain_unwatch_tx */ u8 *towire_onchain_unwatch_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_onchain_unwatch_tx called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* Stubs which do get called. */ diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index c6fa83372..41dadcfdf 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -55,6 +55,13 @@ bool fromwire_onchain_known_preimage(const void *p UNNEEDED, struct preimage *pr /* Generated stub for fromwire_onchain_spent */ bool fromwire_onchain_spent(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct bitcoin_tx **tx UNNEEDED, u32 *input_num UNNEEDED, u32 *blockheight UNNEEDED) { fprintf(stderr, "fromwire_onchain_spent called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for htlc_offered_wscript */ u8 *htlc_offered_wscript(const tal_t *ctx UNNEEDED, const struct ripemd160 *ripemd UNNEEDED, @@ -182,6 +189,12 @@ u8 *towire_onchain_missing_htlc_output(const tal_t *ctx UNNEEDED, const struct h /* Generated stub for towire_onchain_unwatch_tx */ u8 *towire_onchain_unwatch_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_onchain_unwatch_tx called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* Generated stub for wire_sync_read */ u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) { fprintf(stderr, "wire_sync_read called!\n"); abort(); } diff --git a/openingd/opening_wire.csv b/openingd/opening_wire.csv index 23ca203d1..8c5254cd5 100644 --- a/openingd/opening_wire.csv +++ b/openingd/opening_wire.csv @@ -1,11 +1,13 @@ #include #include #include +#include #include msgtype,opening_init,6000 # Which network are we configured for? msgdata,opening_init,chainparams,chainparams, +msgdata,opening_init,feature_set,feature_set, # Base configuration we'll offer (channel reserve will vary with amount) msgdata,opening_init,our_config,channel_config, # Minimum/maximum configuration values we'll accept diff --git a/openingd/openingd.c b/openingd/openingd.c index f74eabe75..6a67c995e 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1481,6 +1481,7 @@ int main(int argc, char *argv[]) struct state *state = tal(NULL, struct state); struct secret *none; struct channel_id *force_tmp_channel_id; + struct feature_set *feature_set; subdaemon_setup(argc, argv); @@ -1492,6 +1493,7 @@ int main(int argc, char *argv[]) msg = wire_sync_read(tmpctx, REQ_FD); if (!fromwire_opening_init(state, msg, &chainparams, + &feature_set, &state->localconf, &state->max_to_self_delay, &state->min_effective_htlc_capacity, @@ -1507,6 +1509,8 @@ int main(int argc, char *argv[]) &dev_fast_gossip)) master_badmsg(WIRE_OPENING_INIT, msg); + features_init(take(feature_set)); + #if DEVELOPER dev_force_tmp_channel_id = force_tmp_channel_id; #endif diff --git a/tools/generate-wire.py b/tools/generate-wire.py index da0e7da36..547c769ef 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -225,6 +225,7 @@ class Type(FieldSet): 'fee_states', 'onionreply', 'witscript', + 'feature_set', ] # Some BOLT types are re-typed based on their field name