diff --git a/eclair-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/eclair/tests/TestConstants.kt b/eclair-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/eclair/tests/TestConstants.kt index f87413042..fe676e1a2 100644 --- a/eclair-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/eclair/tests/TestConstants.kt +++ b/eclair-kmp-test-fixtures/src/commonMain/kotlin/fr/acinq/eclair/tests/TestConstants.kt @@ -41,19 +41,17 @@ object TestConstants { keyManager = keyManager, alias = "alice", features = Features( - setOf( - ActivatedFeature(Feature.InitialRoutingSync, FeatureSupport.Optional), - ActivatedFeature(Feature.OptionDataLossProtect, FeatureSupport.Optional), - ActivatedFeature(Feature.ChannelRangeQueries, FeatureSupport.Optional), - ActivatedFeature(Feature.ChannelRangeQueriesExtended, FeatureSupport.Optional), - ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Optional), - ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional), - ActivatedFeature(Feature.Wumbo, FeatureSupport.Optional), - ActivatedFeature(Feature.StaticRemoteKey, FeatureSupport.Mandatory), - ActivatedFeature(Feature.AnchorOutputs, FeatureSupport.Mandatory), - ActivatedFeature(Feature.TrampolinePayment, FeatureSupport.Optional) - ) + Feature.InitialRoutingSync to FeatureSupport.Optional, + Feature.OptionDataLossProtect to FeatureSupport.Optional, + Feature.ChannelRangeQueries to FeatureSupport.Optional, + Feature.ChannelRangeQueriesExtended to FeatureSupport.Optional, + Feature.VariableLengthOnion to FeatureSupport.Optional, + Feature.PaymentSecret to FeatureSupport.Optional, + Feature.BasicMultiPartPayment to FeatureSupport.Optional, + Feature.Wumbo to FeatureSupport.Optional, + Feature.StaticRemoteKey to FeatureSupport.Mandatory, + Feature.AnchorOutputs to FeatureSupport.Mandatory, + Feature.TrampolinePayment to FeatureSupport.Optional ), dustLimit = 1_100.sat, maxRemoteDustLimit = 1_500.sat, @@ -110,19 +108,17 @@ object TestConstants { keyManager = keyManager, alias = "bob", features = Features( - setOf( - ActivatedFeature(Feature.InitialRoutingSync, FeatureSupport.Optional), - ActivatedFeature(Feature.OptionDataLossProtect, FeatureSupport.Optional), - ActivatedFeature(Feature.ChannelRangeQueries, FeatureSupport.Optional), - ActivatedFeature(Feature.ChannelRangeQueriesExtended, FeatureSupport.Optional), - ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Optional), - ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional), - ActivatedFeature(Feature.Wumbo, FeatureSupport.Optional), - ActivatedFeature(Feature.StaticRemoteKey, FeatureSupport.Mandatory), - ActivatedFeature(Feature.AnchorOutputs, FeatureSupport.Mandatory), - ActivatedFeature(Feature.TrampolinePayment, FeatureSupport.Optional) - ) + Feature.InitialRoutingSync to FeatureSupport.Optional, + Feature.OptionDataLossProtect to FeatureSupport.Optional, + Feature.ChannelRangeQueries to FeatureSupport.Optional, + Feature.ChannelRangeQueriesExtended to FeatureSupport.Optional, + Feature.VariableLengthOnion to FeatureSupport.Optional, + Feature.PaymentSecret to FeatureSupport.Optional, + Feature.BasicMultiPartPayment to FeatureSupport.Optional, + Feature.Wumbo to FeatureSupport.Optional, + Feature.StaticRemoteKey to FeatureSupport.Mandatory, + Feature.AnchorOutputs to FeatureSupport.Mandatory, + Feature.TrampolinePayment to FeatureSupport.Optional ), dustLimit = 1_000.sat, maxRemoteDustLimit = 1_500.sat, diff --git a/src/commonMain/kotlin/fr/acinq/eclair/Features.kt b/src/commonMain/kotlin/fr/acinq/eclair/Features.kt index f1d31e5c3..5f7d0cc4d 100644 --- a/src/commonMain/kotlin/fr/acinq/eclair/Features.kt +++ b/src/commonMain/kotlin/fr/acinq/eclair/Features.kt @@ -101,53 +101,48 @@ sealed class Feature { } } -@Serializable -data class ActivatedFeature(val feature: Feature, val support: FeatureSupport) - @Serializable data class UnknownFeature(val bitIndex: Int) @Serializable -data class Features(val activated: Set, val unknown: Set = emptySet()) { +data class Features(val activated: Map, val unknown: Set = emptySet()) { fun hasFeature(feature: Feature, support: FeatureSupport? = null): Boolean = - if (support != null) activated.contains(ActivatedFeature(feature, support)) - else hasFeature(feature, FeatureSupport.Optional) || hasFeature(feature, FeatureSupport.Mandatory) + if (support != null) activated[feature] == support + else activated.containsKey(feature) /** NB: this method is not reflexive, see [[Features.areCompatible]] if you want symmetric validation. */ fun areSupported(remoteFeatures: Features): Boolean { // we allow unknown odd features (it's ok to be odd) val unknownFeaturesOk = remoteFeatures.unknown.all { it.bitIndex % 2 == 1 } // we verify that we activated every mandatory feature they require - val knownFeaturesOk = remoteFeatures.activated.all { - when (it.support) { + val knownFeaturesOk = remoteFeatures.activated.all { (feature, support) -> + when (support) { FeatureSupport.Optional -> true - FeatureSupport.Mandatory -> hasFeature(it.feature) + FeatureSupport.Mandatory -> hasFeature(feature) } } return unknownFeaturesOk && knownFeaturesOk } fun toByteArray(): ByteArray { - val activatedFeatureBytes = - activated.mapTo(HashSet()) { it.feature.supportBit(it.support) }.indicesToByteArray() - val unknownFeatureBytes = unknown.mapTo(HashSet()) { it.bitIndex }.indicesToByteArray() + val activatedFeatureBytes = activated.map { (feature, support) -> feature.supportBit(support) }.toHashSet().indicesToByteArray() + val unknownFeatureBytes = unknown.map { it.bitIndex }.toHashSet().indicesToByteArray() val maxSize = activatedFeatureBytes.size.coerceAtLeast(unknownFeatureBytes.size) return activatedFeatureBytes.leftPaddedCopyOf(maxSize) or unknownFeatureBytes.leftPaddedCopyOf(maxSize) } private fun Set.indicesToByteArray(): ByteArray { if (isEmpty()) return ByteArray(0) - // When converting from BitVector to ByteVector, scodec pads right instead of left, so we make sure we pad to bytes *before* setting feature bits. val buf = BitField.forAtMost(maxOrNull()!! + 1) forEach { buf.setRight(it) } return buf.bytes } companion object { - val empty = Features(emptySet()) + val empty = Features(mapOf()) - val knownFeatures: Set = setOf( + private val knownFeatures: Set = setOf( Feature.OptionDataLossProtect, Feature.InitialRoutingSync, Feature.ChannelRangeQueries, @@ -169,40 +164,19 @@ data class Features(val activated: Set, val unknown: Set - knownFeatures.find { it.optional == idx }?.let { ActivatedFeature(it, FeatureSupport.Optional) } - ?: knownFeatures.find { it.mandatory == idx } - ?.let { ActivatedFeature(it, FeatureSupport.Mandatory) } + knownFeatures.find { it.optional == idx }?.let { Pair(it, FeatureSupport.Optional) } + ?: knownFeatures.find { it.mandatory == idx }?.let { Pair(it, FeatureSupport.Mandatory) } ?: UnknownFeature(idx) } .toList() return Features( - activated = all.filterIsInstance().toSet(), + activated = all.filterIsInstance>().toMap(), unknown = all.filterIsInstance().toSet() ) } -// /** expects to have a top level config block named "features" */ -// fun fromConfiguration(config: Config): Features = Features( -// knownFeatures.flatMap { -// feature => -// getFeature(config, feature.rfcName) match { -// case Some(support) => Some(ActivatedFeature(feature, support)) -// case _ => None -// } -// }) -// -// /** tries to extract the given feature name from the config, if successful returns its feature support */ -// private def getFeature(config: Config, name: String): Option[FeatureSupport] = { -// if (!config.hasPath(s"features.$name")) None -// else { -// config.getString(s"features.$name") match { -// case support if support == Mandatory.toString => Some(Mandatory) -// case support if support == Optional.toString => Some(Optional) -// case wrongSupport => throw new IllegalArgumentException(s"Wrong support specified ($wrongSupport)") -// } -// } -// } + operator fun invoke(vararg pairs: Pair): Features = Features(mapOf(*pairs)) // Features may depend on other features, as specified in Bolt 9. private val featuresDependency: Map> = mapOf( @@ -229,7 +203,6 @@ data class Features(val activated: Set, val unknown: Set bolt11Features.contains(f.feature) }.toSet()) + return Features(activated = features.activated.filter { (f, _) -> bolt11Features.contains(f) }) } fun create( diff --git a/src/commonTest/kotlin/fr/acinq/eclair/FeaturesTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/eclair/FeaturesTestsCommon.kt index e9e1fccf5..37d3b4dcf 100644 --- a/src/commonTest/kotlin/fr/acinq/eclair/FeaturesTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/eclair/FeaturesTestsCommon.kt @@ -23,11 +23,9 @@ class FeaturesTestsCommon : EclairTestSuite() { @Test fun `'initial_routing_sync', 'data_loss_protect' and 'variable_length_onion' features`() { val features = Features( - setOf( - ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), - ActivatedFeature(OptionDataLossProtect, FeatureSupport.Optional), - ActivatedFeature(VariableLengthOnion, FeatureSupport.Mandatory) - ) + InitialRoutingSync to FeatureSupport.Optional, + OptionDataLossProtect to FeatureSupport.Optional, + VariableLengthOnion to FeatureSupport.Mandatory ) assertTrue(features.toByteArray().contentEquals(byteArrayOf(0x01, 0x0a))) assertTrue(features.hasFeature(OptionDataLossProtect)) @@ -94,22 +92,22 @@ class FeaturesTestsCommon : EclairTestSuite() { ), TestCase( Features.empty, - Features(setOf(ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional))), + Features(InitialRoutingSync to FeatureSupport.Optional, VariableLengthOnion to FeatureSupport.Optional), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true ), TestCase( Features.empty, - Features(setOf(), setOf(UnknownFeature(101), UnknownFeature(103))), + Features(mapOf(), setOf(UnknownFeature(101), UnknownFeature(103))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true ), // Same feature set TestCase( - Features(setOf(ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Mandatory))), - Features(setOf(ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Mandatory))), + Features(InitialRoutingSync to FeatureSupport.Optional, VariableLengthOnion to FeatureSupport.Mandatory), + Features(InitialRoutingSync to FeatureSupport.Optional, VariableLengthOnion to FeatureSupport.Mandatory), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true @@ -117,19 +115,15 @@ class FeaturesTestsCommon : EclairTestSuite() { // Many optional features TestCase( Features( - setOf( - ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), - ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(ChannelRangeQueries, FeatureSupport.Optional), - ActivatedFeature(PaymentSecret, FeatureSupport.Optional) - ) + InitialRoutingSync to FeatureSupport.Optional, + VariableLengthOnion to FeatureSupport.Optional, + ChannelRangeQueries to FeatureSupport.Optional, + PaymentSecret to FeatureSupport.Optional ), Features( - setOf( - ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(ChannelRangeQueries, FeatureSupport.Optional), - ActivatedFeature(ChannelRangeQueriesExtended, FeatureSupport.Optional) - ) + VariableLengthOnion to FeatureSupport.Optional, + ChannelRangeQueries to FeatureSupport.Optional, + ChannelRangeQueriesExtended to FeatureSupport.Optional ), oursSupportTheirs = true, theirsSupportOurs = true, @@ -137,57 +131,57 @@ class FeaturesTestsCommon : EclairTestSuite() { ), // We support their mandatory features TestCase( - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional))), - Features(setOf(ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), ActivatedFeature(VariableLengthOnion, FeatureSupport.Mandatory))), + Features(VariableLengthOnion to FeatureSupport.Optional), + Features(InitialRoutingSync to FeatureSupport.Optional, VariableLengthOnion to FeatureSupport.Mandatory), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true ), // They support our mandatory features TestCase( - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Mandatory))), - Features(setOf(ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional))), + Features(VariableLengthOnion to FeatureSupport.Mandatory), + Features(InitialRoutingSync to FeatureSupport.Optional, VariableLengthOnion to FeatureSupport.Optional), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true ), // They have unknown optional features TestCase( - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional))), - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional)), setOf(UnknownFeature(141))), + Features(VariableLengthOnion to FeatureSupport.Optional), + Features(mapOf(VariableLengthOnion to FeatureSupport.Optional), setOf(UnknownFeature(141))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true ), // They have unknown mandatory features TestCase( - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional))), - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional)), setOf(UnknownFeature(142))), + Features(VariableLengthOnion to FeatureSupport.Optional), + Features(mapOf(VariableLengthOnion to FeatureSupport.Optional), setOf(UnknownFeature(142))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false ), // We don't support one of their mandatory features TestCase( - Features(setOf(ActivatedFeature(ChannelRangeQueries, FeatureSupport.Optional))), - Features(setOf(ActivatedFeature(ChannelRangeQueries, FeatureSupport.Mandatory), ActivatedFeature(VariableLengthOnion, FeatureSupport.Mandatory))), + Features(ChannelRangeQueries to FeatureSupport.Optional), + Features(ChannelRangeQueries to FeatureSupport.Mandatory, VariableLengthOnion to FeatureSupport.Mandatory), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false ), // They don't support one of our mandatory features TestCase( - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Mandatory), ActivatedFeature(PaymentSecret, FeatureSupport.Mandatory))), - Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional))), + Features(VariableLengthOnion to FeatureSupport.Mandatory, PaymentSecret to FeatureSupport.Mandatory), + Features(VariableLengthOnion to FeatureSupport.Optional), oursSupportTheirs = true, theirsSupportOurs = false, compatible = false ), // nonreg testing of future features (needs to be updated with every new supported mandatory bit) - TestCase(Features.empty, Features(setOf(), setOf(UnknownFeature(22))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false), - TestCase(Features.empty, Features(setOf(), setOf(UnknownFeature(23))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true), - TestCase(Features.empty, Features(setOf(), setOf(UnknownFeature(24))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false), - TestCase(Features.empty, Features(setOf(), setOf(UnknownFeature(25))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true) + TestCase(Features.empty, Features(mapOf(), setOf(UnknownFeature(22))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false), + TestCase(Features.empty, Features(mapOf(), setOf(UnknownFeature(23))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true), + TestCase(Features.empty, Features(mapOf(), setOf(UnknownFeature(24))), oursSupportTheirs = false, theirsSupportOurs = true, compatible = false), + TestCase(Features.empty, Features(mapOf(), setOf(UnknownFeature(25))), oursSupportTheirs = true, theirsSupportOurs = true, compatible = true) ) testCases.forEach { testCase -> @@ -201,27 +195,25 @@ class FeaturesTestsCommon : EclairTestSuite() { fun `features to bytes`() { val testCases = mapOf( byteArrayOf() to Features.empty, - byteArrayOf(0x01, 0x00) to Features(setOf(ActivatedFeature(VariableLengthOnion, FeatureSupport.Mandatory))), + byteArrayOf(0x01, 0x00) to Features(VariableLengthOnion to FeatureSupport.Mandatory), byteArrayOf(0x02, 0x8a.toByte(), 0x8a.toByte()) to Features( - setOf( - ActivatedFeature(OptionDataLossProtect, FeatureSupport.Optional), - ActivatedFeature(InitialRoutingSync, FeatureSupport.Optional), - ActivatedFeature(ChannelRangeQueries, FeatureSupport.Optional), - ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(ChannelRangeQueriesExtended, FeatureSupport.Optional), - ActivatedFeature(PaymentSecret, FeatureSupport.Optional), - ActivatedFeature(BasicMultiPartPayment, FeatureSupport.Optional) - ) + OptionDataLossProtect to FeatureSupport.Optional, + InitialRoutingSync to FeatureSupport.Optional, + ChannelRangeQueries to FeatureSupport.Optional, + VariableLengthOnion to FeatureSupport.Optional, + ChannelRangeQueriesExtended to FeatureSupport.Optional, + PaymentSecret to FeatureSupport.Optional, + BasicMultiPartPayment to FeatureSupport.Optional ), byteArrayOf(0x09, 0x00, 0x42, 0x00) to Features( - setOf( - ActivatedFeature(VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(PaymentSecret, FeatureSupport.Mandatory) + mapOf( + VariableLengthOnion to FeatureSupport.Optional, + PaymentSecret to FeatureSupport.Mandatory ), setOf(UnknownFeature(24), UnknownFeature(27)) ), byteArrayOf(0x52, 0x00, 0x00, 0x00) to Features( - emptySet(), + mapOf(), setOf(UnknownFeature(25), UnknownFeature(28), UnknownFeature(30)) ) ) @@ -235,106 +227,4 @@ class FeaturesTestsCommon : EclairTestSuite() { } } -// @Test fun `parse features from configuration`() { -// { -// val conf = ConfigFactory.parseString( -// """ -// |features { -// | option_data_loss_protect = optional -// | initial_routing_sync = optional -// | gossip_queries = optional -// | gossip_queries_ex = optional -// | var_onion_optin = optional -// | payment_secret = optional -// | basic_mpp = optional -// |} -// """.stripMargin) -// -// val features = fromConfiguration(conf) -// assertTrue(features.toByteVector === hex"028a8a") -// assertTrue(Features(hex"028a8a") === features) -// assertTrue(areSupported(features)) -// assertTrue(validateFeatureGraph(features) === None) -// assertTrue(features.hasFeature(OptionDataLossProtect, Some(Optional))) -// assertTrue(features.hasFeature(InitialRoutingSync, Some(Optional))) -// assertTrue(features.hasFeature(ChannelRangeQueries, Some(Optional))) -// assertTrue(features.hasFeature(ChannelRangeQueriesExtended, Some(Optional))) -// assertTrue(features.hasFeature(VariableLengthOnion, Some(Optional))) -// assertTrue(features.hasFeature(PaymentSecret, Some(Optional))) -// assertTrue(features.hasFeature(BasicMultiPartPayment, Some(Optional))) -// } -// -// { -// val conf = ConfigFactory.parseString( -// """ -// | features { -// | initial_routing_sync = optional -// | option_data_loss_protect = optional -// | gossip_queries = optional -// | gossip_queries_ex = mandatory -// | var_onion_optin = optional -// | } -// | -// """.stripMargin -// ) -// -// val features = fromConfiguration(conf) -// assertTrue(features.toByteVector === hex"068a") -// assertTrue(Features(hex"068a") === features) -// assertTrue(areSupported(features)) -// assertTrue(validateFeatureGraph(features) === None) -// assertTrue(features.hasFeature(OptionDataLossProtect, Some(Optional))) -// assertTrue(features.hasFeature(InitialRoutingSync, Some(Optional))) -// assertTrue(!features.hasFeature(InitialRoutingSync, Some(Mandatory))) -// assertTrue(features.hasFeature(ChannelRangeQueries, Some(Optional))) -// assertTrue(features.hasFeature(ChannelRangeQueriesExtended, Some(Mandatory))) -// assertTrue(features.hasFeature(VariableLengthOnion, Some(Optional))) -// assertTrue(!features.hasFeature(PaymentSecret)) -// } -// -// { -// val confWithUnknownFeatures = ConfigFactory.parseString( -// """ -// |features { -// | option_non_existent = mandatory # this is ignored -// | gossip_queries = optional -// | payment_secret = mandatory -// |} -// """.stripMargin) -// -// val features = fromConfiguration(confWithUnknownFeatures) -// assertTrue(features.toByteVector === hex"4080") -// assertTrue(Features(hex"4080") === features) -// assertTrue(areSupported(features)) -// assertTrue(features.hasFeature(ChannelRangeQueries, Some(Optional))) -// assertTrue(features.hasFeature(PaymentSecret, Some(Mandatory))) -// } -// -// { -// val confWithUnknownSupport = ConfigFactory.parseString( -// """ -// |features { -// | option_data_loss_protect = what -// | gossip_queries = optional -// | payment_secret = mandatory -// |} -// """.stripMargin) -// -// assertThrows[RuntimeException](fromConfiguration(confWithUnknownSupport)) -// } -// } -// -// @Test fun `'knownFeatures' contains all our known features (reflection test)`() { -// import scala.reflect.runtime.universe._ -// import scala.reflect.runtime.{ universe => runtime } -// val mirror = runtime.runtimeMirror(ClassLoader.getSystemClassLoader) -// val subclasses = typeOf[Feature].typeSymbol.asClass.knownDirectSubclasses -// val knownFeatures = subclasses.map({ desc => -// val mod = mirror.staticModule(desc.asClass.fullName) -// mirror.reflectModule(mod).instance.asInstanceOf[Feature] -// }) -// -// assertTrue((knownFeatures -- Features.knownFeatures).isEmpty) -// } - } diff --git a/src/commonTest/kotlin/fr/acinq/eclair/db/PaymentsDbTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/eclair/db/PaymentsDbTestsCommon.kt index cb565f958..cc4bf186a 100644 --- a/src/commonTest/kotlin/fr/acinq/eclair/db/PaymentsDbTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/eclair/db/PaymentsDbTestsCommon.kt @@ -320,11 +320,9 @@ class PaymentsDbTestsCommon : EclairTestSuite() { companion object { private val defaultFeatures = Features( - setOf( - ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Optional), - ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional) - ) + Feature.VariableLengthOnion to FeatureSupport.Optional, + Feature.PaymentSecret to FeatureSupport.Optional, + Feature.BasicMultiPartPayment to FeatureSupport.Optional ) private fun createFixture(): Triple { diff --git a/src/commonTest/kotlin/fr/acinq/eclair/payment/OutgoingPaymentHandlerTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/eclair/payment/OutgoingPaymentHandlerTestsCommon.kt index 5ddb59e06..26a48d923 100644 --- a/src/commonTest/kotlin/fr/acinq/eclair/payment/OutgoingPaymentHandlerTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/eclair/payment/OutgoingPaymentHandlerTestsCommon.kt @@ -812,13 +812,13 @@ class OutgoingPaymentHandlerTestsCommon : EclairTestSuite() { val paymentPreimage: ByteVector32 = randomBytes32() val paymentHash = Crypto.sha256(paymentPreimage).toByteVector32() - val invoiceFeatures = mutableSetOf( - ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Mandatory), - ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional) + val invoiceFeatures = mutableMapOf( + Feature.VariableLengthOnion to FeatureSupport.Optional, + Feature.PaymentSecret to FeatureSupport.Mandatory, + Feature.BasicMultiPartPayment to FeatureSupport.Optional ) if (supportsTrampoline) { - invoiceFeatures.add(ActivatedFeature(Feature.TrampolinePayment, FeatureSupport.Optional)) + invoiceFeatures[Feature.TrampolinePayment] = FeatureSupport.Optional } return PaymentRequest.create( @@ -828,7 +828,7 @@ class OutgoingPaymentHandlerTestsCommon : EclairTestSuite() { privateKey = privKey, description = "unit test", minFinalCltvExpiryDelta = PaymentRequest.DEFAULT_MIN_FINAL_EXPIRY_DELTA, - features = Features(invoiceFeatures), + features = Features(invoiceFeatures.toMap()), extraHops = extraHops ) } diff --git a/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentPacketTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentPacketTestsCommon.kt index 4d5e08916..ece807247 100644 --- a/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentPacketTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentPacketTestsCommon.kt @@ -276,8 +276,7 @@ class PaymentPacketTestsCommon : EclairTestSuite() { // a -> b -> c d -> e val routingHints = listOf(PaymentRequest.TaggedField.ExtraHop(randomKey().publicKey(), ShortChannelId(42), 10.msat, 100, CltvExpiryDelta(144))) - val invoiceFeatures = - Features(setOf(ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Optional), ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional))) + val invoiceFeatures = Features(Feature.VariableLengthOnion to FeatureSupport.Optional, Feature.PaymentSecret to FeatureSupport.Optional, Feature.BasicMultiPartPayment to FeatureSupport.Optional) val invoice = PaymentRequest( "lnbcrt", finalAmount, currentTimestampSeconds(), e, listOf( PaymentRequest.TaggedField.PaymentHash(paymentHash), diff --git a/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentRequestTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentRequestTestsCommon.kt index 8cd155161..04634546a 100644 --- a/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentRequestTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/eclair/payment/PaymentRequestTestsCommon.kt @@ -388,7 +388,7 @@ class PaymentRequestTestsCommon : EclairTestSuite() { @Test fun `payment secret`() { val features = - Features(setOf(ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Optional), ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional))) + Features(Feature.VariableLengthOnion to FeatureSupport.Optional, Feature.PaymentSecret to FeatureSupport.Optional, Feature.BasicMultiPartPayment to FeatureSupport.Optional) val pr = PaymentRequest.create(Block.LivenetGenesisBlock.hash, 123.msat, ByteVector32.One, priv, "Some invoice", CltvExpiryDelta(18), features) assertNotNull(pr.paymentSecret) assertEquals(ByteVector("028200"), pr.features) @@ -412,7 +412,7 @@ class PaymentRequestTestsCommon : EclairTestSuite() { priv, "MPP without secrets", CltvExpiryDelta(18), - Features(setOf(ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional))) + Features(Feature.VariableLengthOnion to FeatureSupport.Optional, Feature.BasicMultiPartPayment to FeatureSupport.Optional) ) } } @@ -490,11 +490,11 @@ class PaymentRequestTestsCommon : EclairTestSuite() { assertEquals( expected = PaymentRequest.invoiceFeatures( Features( - activated = setOf( - ActivatedFeature(Feature.InitialRoutingSync, FeatureSupport.Optional), - ActivatedFeature(Feature.StaticRemoteKey, FeatureSupport.Mandatory), - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Mandatory), - ActivatedFeature(Feature.TrampolinePayment, FeatureSupport.Optional), + activated = mapOf( + Feature.InitialRoutingSync to FeatureSupport.Optional, + Feature.StaticRemoteKey to FeatureSupport.Mandatory, + Feature.PaymentSecret to FeatureSupport.Mandatory, + Feature.TrampolinePayment to FeatureSupport.Optional, ), unknown = setOf( UnknownFeature(47) @@ -502,9 +502,9 @@ class PaymentRequestTestsCommon : EclairTestSuite() { ) ), actual = Features( - activated = setOf( - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Mandatory), - ActivatedFeature(Feature.TrampolinePayment, FeatureSupport.Optional) + activated = mapOf( + Feature.PaymentSecret to FeatureSupport.Mandatory, + Feature.TrampolinePayment to FeatureSupport.Optional ) ) ) diff --git a/src/jvmTest/kotlin/fr/acinq/eclair/Node.kt b/src/jvmTest/kotlin/fr/acinq/eclair/Node.kt index 8ae1339d4..696181468 100644 --- a/src/jvmTest/kotlin/fr/acinq/eclair/Node.kt +++ b/src/jvmTest/kotlin/fr/acinq/eclair/Node.kt @@ -140,16 +140,14 @@ object Node { keyManager = keyManager, alias = "phoenix", features = Features( - setOf( - ActivatedFeature(Feature.OptionDataLossProtect, FeatureSupport.Mandatory), - ActivatedFeature(Feature.VariableLengthOnion, FeatureSupport.Optional), - ActivatedFeature(Feature.PaymentSecret, FeatureSupport.Optional), - ActivatedFeature(Feature.BasicMultiPartPayment, FeatureSupport.Optional), - ActivatedFeature(Feature.Wumbo, FeatureSupport.Optional), - ActivatedFeature(Feature.StaticRemoteKey, FeatureSupport.Optional), - ActivatedFeature(Feature.AnchorOutputs, FeatureSupport.Optional), - ActivatedFeature(Feature.TrampolinePayment, FeatureSupport.Optional), - ) + Feature.OptionDataLossProtect to FeatureSupport.Mandatory, + Feature.VariableLengthOnion to FeatureSupport.Optional, + Feature.PaymentSecret to FeatureSupport.Optional, + Feature.BasicMultiPartPayment to FeatureSupport.Optional, + Feature.Wumbo to FeatureSupport.Optional, + Feature.StaticRemoteKey to FeatureSupport.Optional, + Feature.AnchorOutputs to FeatureSupport.Optional, + Feature.TrampolinePayment to FeatureSupport.Optional, ), dustLimit = 546.sat, maxRemoteDustLimit = 600.sat,