Improve support for plugin-defined features#3264
Merged
Conversation
We allow injecting non-official features via plugins, by extending the `CustomFeaturePlugin` trait. However, we previously added those features to a set of unknown features: we can do better and add them to the known features map, which provides pretty-printing in logs and simplifies debugging. We also had an issue where `hasFeature` would always return `false` for plugin features, even when they were supported by both nodes, because we only looked at our `activated` features map which only contains official features. We get rid of the `UnknownFeature` class entirely and keep the encoded feature bits after deserializing them. We take those feature bits into account in `hasFeature`, which fixes the issue. This creates a slightly weird asymmetry because our local features, which are created based on our `eclair.conf`, will not have this field set while features that are read from network messages or the DB will have this field set. A better architecture would be to remove the `activated` map entirely and directly wrap the feature bits, while providing helper functions to interact with features without having to directly read a `BitVector`. This is a larger refactoring though, because we'd need to add support for filtering `init` / `node` features using bitmasks, which isn't entirely trivial to handle and requires careful management of plugin features.
pm47
reviewed
Mar 17, 2026
Member
pm47
left a comment
There was a problem hiding this comment.
I think we should use ByteVector instead of BitVector to store encoded features:
- it saves error-prone padding
- we will always get byte-aligned bits from the network anyway
- we can always convert to bits inside our methods when convenient
Note that we still need a constructor from `BitVector` for Bolt 11 invoices which use a 5-bit encoding.
pm47
approved these changes
Mar 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We allow injecting non-official features via plugins, by extending the
CustomFeaturePlugintrait. However, we previously added those features to a set of unknown features: we can do better and add them to the known features map, which provides pretty-printing in logs and simplifies debugging.We also had an issue where
hasFeaturewould always returnfalsefor plugin features, even when they were supported by both nodes, because we only looked at ouractivatedfeatures map which only contains official features.We get rid of the
UnknownFeatureclass entirely and keep the encoded feature bits after deserializing them. We take those feature bits into account inhasFeature, which fixes the issue.This creates a slightly weird asymmetry because our local features, which are created based on our
eclair.conf, will not have this field set while features that are read from network messages or the DB will have this field set. A better architecture would be to remove theactivatedmap entirely and directly wrap the feature bits, while providing helper functions to interact with features without having to directly read aBitVector. This is a larger refactoring though, because we'd need to add support for filteringinit/nodefeatures using bitmasks, which isn't entirely trivial to handle and requires careful management when there are plugin features.