@@ -101,53 +101,48 @@ sealed class Feature {
101101 }
102102}
103103
104- @Serializable
105- data class ActivatedFeature (val feature : Feature , val support : FeatureSupport )
106-
107104@Serializable
108105data class UnknownFeature (val bitIndex : Int )
109106
110107@Serializable
111- data class Features (val activated : Set < ActivatedFeature >, val unknown : Set <UnknownFeature > = emptySet()) {
108+ data class Features (val activated : Map < Feature , FeatureSupport >, val unknown : Set <UnknownFeature > = emptySet()) {
112109
113110 fun hasFeature (feature : Feature , support : FeatureSupport ? = null): Boolean =
114- if (support != null ) activated.contains( ActivatedFeature ( feature, support))
115- else hasFeature(feature, FeatureSupport . Optional ) || hasFeature (feature, FeatureSupport . Mandatory )
111+ if (support != null ) activated[ feature] == support
112+ else activated.containsKey (feature)
116113
117114 /* * NB: this method is not reflexive, see [[Features.areCompatible]] if you want symmetric validation. */
118115 fun areSupported (remoteFeatures : Features ): Boolean {
119116 // we allow unknown odd features (it's ok to be odd)
120117 val unknownFeaturesOk = remoteFeatures.unknown.all { it.bitIndex % 2 == 1 }
121118 // we verify that we activated every mandatory feature they require
122- val knownFeaturesOk = remoteFeatures.activated.all {
123- when (it. support) {
119+ val knownFeaturesOk = remoteFeatures.activated.all { (feature, support) ->
120+ when (support) {
124121 FeatureSupport .Optional -> true
125- FeatureSupport .Mandatory -> hasFeature(it. feature)
122+ FeatureSupport .Mandatory -> hasFeature(feature)
126123 }
127124 }
128125 return unknownFeaturesOk && knownFeaturesOk
129126 }
130127
131128 fun toByteArray (): ByteArray {
132- val activatedFeatureBytes =
133- activated.mapTo(HashSet ()) { it.feature.supportBit(it.support) }.indicesToByteArray()
134- val unknownFeatureBytes = unknown.mapTo(HashSet ()) { it.bitIndex }.indicesToByteArray()
129+ val activatedFeatureBytes = activated.map { (feature, support) -> feature.supportBit(support) }.toHashSet().indicesToByteArray()
130+ val unknownFeatureBytes = unknown.map { it.bitIndex }.toHashSet().indicesToByteArray()
135131 val maxSize = activatedFeatureBytes.size.coerceAtLeast(unknownFeatureBytes.size)
136132 return activatedFeatureBytes.leftPaddedCopyOf(maxSize) or unknownFeatureBytes.leftPaddedCopyOf(maxSize)
137133 }
138134
139135 private fun Set<Int>.indicesToByteArray (): ByteArray {
140136 if (isEmpty()) return ByteArray (0 )
141- // When converting from BitVector to ByteVector, scodec pads right instead of left, so we make sure we pad to bytes *before* setting feature bits.
142137 val buf = BitField .forAtMost(maxOrNull()!! + 1 )
143138 forEach { buf.setRight(it) }
144139 return buf.bytes
145140 }
146141
147142 companion object {
148- val empty = Features (emptySet ())
143+ val empty = Features (mapOf ())
149144
150- val knownFeatures: Set <Feature > = setOf (
145+ private val knownFeatures: Set <Feature > = setOf (
151146 Feature .OptionDataLossProtect ,
152147 Feature .InitialRoutingSync ,
153148 Feature .ChannelRangeQueries ,
@@ -169,40 +164,19 @@ data class Features(val activated: Set<ActivatedFeature>, val unknown: Set<Unkno
169164 val all = bits.asRightSequence().withIndex()
170165 .filter { it.value }
171166 .map { (idx, _) ->
172- knownFeatures.find { it.optional == idx }?.let { ActivatedFeature (it, FeatureSupport .Optional ) }
173- ? : knownFeatures.find { it.mandatory == idx }
174- ?.let { ActivatedFeature (it, FeatureSupport .Mandatory ) }
167+ knownFeatures.find { it.optional == idx }?.let { Pair (it, FeatureSupport .Optional ) }
168+ ? : knownFeatures.find { it.mandatory == idx }?.let { Pair (it, FeatureSupport .Mandatory ) }
175169 ? : UnknownFeature (idx)
176170 }
177171 .toList()
178172
179173 return Features (
180- activated = all.filterIsInstance<ActivatedFeature > ().toSet (),
174+ activated = all.filterIsInstance<Pair < Feature , FeatureSupport >> ().toMap (),
181175 unknown = all.filterIsInstance<UnknownFeature >().toSet()
182176 )
183177 }
184178
185- // /** expects to have a top level config block named "features" */
186- // fun fromConfiguration(config: Config): Features = Features(
187- // knownFeatures.flatMap {
188- // feature =>
189- // getFeature(config, feature.rfcName) match {
190- // case Some(support) => Some(ActivatedFeature(feature, support))
191- // case _ => None
192- // }
193- // })
194- //
195- // /** tries to extract the given feature name from the config, if successful returns its feature support */
196- // private def getFeature(config: Config, name: String): Option[FeatureSupport] = {
197- // if (!config.hasPath(s"features.$name")) None
198- // else {
199- // config.getString(s"features.$name") match {
200- // case support if support == Mandatory.toString => Some(Mandatory)
201- // case support if support == Optional.toString => Some(Optional)
202- // case wrongSupport => throw new IllegalArgumentException(s"Wrong support specified ($wrongSupport)")
203- // }
204- // }
205- // }
179+ operator fun invoke (vararg pairs : Pair <Feature , FeatureSupport >): Features = Features (mapOf (* pairs))
206180
207181 // Features may depend on other features, as specified in Bolt 9.
208182 private val featuresDependency: Map <Feature , List <Feature >> = mapOf (
@@ -229,7 +203,6 @@ data class Features(val activated: Set<ActivatedFeature>, val unknown: Set<Unkno
229203
230204 fun areCompatible (ours : Features , theirs : Features ): Boolean = ours.areSupported(theirs) && theirs.areSupported(ours)
231205
232-
233206 /* * returns true if both have at least optional support */
234207 fun canUseFeature (localFeatures : Features , remoteFeatures : Features , feature : Feature ): Boolean =
235208 localFeatures.hasFeature(feature) && remoteFeatures.hasFeature(feature)
0 commit comments