Skip to content

Commit 9141998

Browse files
authored
Make payment_secret mandatory (#1810)
This is a security feature that has been introduced a long time ago and is widely supported across the network. We can safely make it mandatory which closes probing attack vectors.
1 parent 9c3ee59 commit 9141998

14 files changed

+63
-46
lines changed

eclair-core/src/main/resources/reference.conf

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ eclair {
4444
option_data_loss_protect = optional
4545
gossip_queries = optional
4646
gossip_queries_ex = optional
47-
var_onion_optin = optional
47+
var_onion_optin = mandatory
4848
option_static_remotekey = optional
49-
payment_secret = optional
49+
payment_secret = mandatory
5050
basic_mpp = optional
5151
option_support_large_channel = optional
5252
option_anchor_outputs = disabled

eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ object NodeParams extends Logging {
236236
def validateFeatures(features: Features): Unit = {
237237
val featuresErr = Features.validateFeatureGraph(features)
238238
require(featuresErr.isEmpty, featuresErr.map(_.message))
239-
require(features.hasFeature(Features.VariableLengthOnion), s"${Features.VariableLengthOnion.rfcName} must be enabled")
239+
require(features.hasFeature(Features.VariableLengthOnion, Some(FeatureSupport.Mandatory)), s"${Features.VariableLengthOnion.rfcName} must be enabled and mandatory")
240+
require(features.hasFeature(Features.PaymentSecret, Some(FeatureSupport.Mandatory)), s"${Features.PaymentSecret.rfcName} must be enabled and mandatory")
240241
require(!features.hasFeature(Features.InitialRoutingSync), s"${Features.InitialRoutingSync.rfcName} is not supported anymore, use ${Features.ChannelRangeQueries.rfcName} instead")
241242
}
242243

eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ object PaymentRequest {
135135
expirySeconds: Option[Long] = None,
136136
extraHops: List[List[ExtraHop]] = Nil,
137137
timestamp: Long = System.currentTimeMillis() / 1000L,
138-
features: Option[PaymentRequestFeatures] = Some(PaymentRequestFeatures(Features.VariableLengthOnion.optional, Features.PaymentSecret.optional))): PaymentRequest = {
138+
features: Option[PaymentRequestFeatures] = Some(PaymentRequestFeatures(Features.VariableLengthOnion.mandatory, Features.PaymentSecret.mandatory))): PaymentRequest = {
139139

140140
val prefix = prefixes(chainHash)
141141
val tags = {

eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/MultiPartHandler.scala

+3-5
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,8 @@ class MultiPartHandler(nodeParams: NodeParams, register: ActorRef, db: IncomingP
5757
val paymentPreimage = paymentPreimage_opt.getOrElse(randomBytes32)
5858
val paymentHash = Crypto.sha256(paymentPreimage)
5959
val expirySeconds = expirySeconds_opt.getOrElse(nodeParams.paymentRequestExpiry.toSeconds)
60-
// We currently only optionally support payment secrets (to allow legacy clients to pay invoices).
61-
// Once we're confident most of the network has upgraded, we should switch to mandatory payment secrets.
6260
val features = {
63-
val f1 = Seq(Features.PaymentSecret.optional, Features.VariableLengthOnion.optional)
61+
val f1 = Seq(Features.PaymentSecret.mandatory, Features.VariableLengthOnion.mandatory)
6462
val allowMultiPart = nodeParams.features.hasFeature(Features.BasicMultiPartPayment)
6563
val f2 = if (allowMultiPart) Seq(Features.BasicMultiPartPayment.optional) else Nil
6664
val f3 = if (nodeParams.enableTrampolinePayment) Seq(Features.TrampolinePayment.optional) else Nil
@@ -99,9 +97,9 @@ class MultiPartHandler(nodeParams: NodeParams, register: ActorRef, db: IncomingP
9997
val paymentHash = Crypto.sha256(paymentPreimage)
10098
val desc = "Donation"
10199
val features = if (nodeParams.features.hasFeature(Features.BasicMultiPartPayment)) {
102-
PaymentRequestFeatures(Features.BasicMultiPartPayment.optional, Features.PaymentSecret.optional, Features.VariableLengthOnion.optional)
100+
PaymentRequestFeatures(Features.BasicMultiPartPayment.optional, Features.PaymentSecret.mandatory, Features.VariableLengthOnion.mandatory)
103101
} else {
104-
PaymentRequestFeatures(Features.PaymentSecret.optional, Features.VariableLengthOnion.optional)
102+
PaymentRequestFeatures(Features.PaymentSecret.mandatory, Features.VariableLengthOnion.mandatory)
105103
}
106104

107105
// Insert a fake invoice and then restart the incoming payment handler

eclair-core/src/test/scala/fr/acinq/eclair/StartupSpec.scala

+24-7
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ class StartupSpec extends AnyFunSuite {
9494
s"features.${OptionDataLossProtect.rfcName}" -> "optional",
9595
s"features.${ChannelRangeQueries.rfcName}" -> "optional",
9696
s"features.${ChannelRangeQueriesExtended.rfcName}" -> "optional",
97-
s"features.${VariableLengthOnion.rfcName}" -> "optional",
98-
s"features.${PaymentSecret.rfcName}" -> "optional",
97+
s"features.${VariableLengthOnion.rfcName}" -> "mandatory",
98+
s"features.${PaymentSecret.rfcName}" -> "mandatory",
9999
s"features.${BasicMultiPartPayment.rfcName}" -> "optional"
100100
).asJava)
101101

@@ -106,22 +106,39 @@ class StartupSpec extends AnyFunSuite {
106106
s"features.${ChannelRangeQueriesExtended.rfcName}" -> "optional"
107107
).asJava)
108108

109+
// var_onion_optin cannot be optional
110+
val optionalVarOnionOptinConf = ConfigFactory.parseMap(Map(
111+
s"features.${OptionDataLossProtect.rfcName}" -> "optional",
112+
s"features.${VariableLengthOnion.rfcName}" -> "optional"
113+
).asJava)
114+
115+
// payment_secret cannot be optional
116+
val optionalPaymentSecretConf = ConfigFactory.parseMap(Map(
117+
s"features.${OptionDataLossProtect.rfcName}" -> "optional",
118+
s"features.${VariableLengthOnion.rfcName}" -> "mandatory",
119+
s"features.${PaymentSecret.rfcName}" -> "optional",
120+
).asJava)
121+
109122
// initial_routing_sync cannot be enabled
110123
val initialRoutingSyncConf = ConfigFactory.parseMap(Map(
111124
s"features.${OptionDataLossProtect.rfcName}" -> "optional",
112125
s"features.${InitialRoutingSync.rfcName}" -> "optional",
113126
s"features.${ChannelRangeQueries.rfcName}" -> "optional",
114127
s"features.${ChannelRangeQueriesExtended.rfcName}" -> "optional",
115-
s"features.${VariableLengthOnion.rfcName}" -> "optional"
128+
s"features.${VariableLengthOnion.rfcName}" -> "mandatory",
129+
s"features.${PaymentSecret.rfcName}" -> "mandatory",
116130
).asJava)
117131

118-
// basic_mpp without payment_secret
132+
// extended channel queries without channel queries
119133
val illegalFeaturesConf = ConfigFactory.parseMap(Map(
120-
s"features.${BasicMultiPartPayment.rfcName}" -> "optional"
134+
s"features.${OptionDataLossProtect.rfcName}" -> "optional",
135+
s"features.${ChannelRangeQueriesExtended.rfcName}" -> "optional"
121136
).asJava)
122137

123138
assert(Try(makeNodeParamsWithDefaults(finalizeConf(legalFeaturesConf))).isSuccess)
124139
assert(Try(makeNodeParamsWithDefaults(finalizeConf(noVariableLengthOnionConf))).isFailure)
140+
assert(Try(makeNodeParamsWithDefaults(finalizeConf(optionalVarOnionOptinConf))).isFailure)
141+
assert(Try(makeNodeParamsWithDefaults(finalizeConf(optionalPaymentSecretConf))).isFailure)
125142
assert(Try(makeNodeParamsWithDefaults(finalizeConf(initialRoutingSyncConf))).isFailure)
126143
assert(Try(makeNodeParamsWithDefaults(finalizeConf(illegalFeaturesConf))).isFailure)
127144
}
@@ -133,7 +150,7 @@ class StartupSpec extends AnyFunSuite {
133150
| {
134151
| nodeid = "02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
135152
| features {
136-
| var_onion_optin = optional
153+
| var_onion_optin = mandatory
137154
| payment_secret = mandatory
138155
| basic_mpp = mandatory
139156
| }
@@ -144,7 +161,7 @@ class StartupSpec extends AnyFunSuite {
144161

145162
val nodeParams = makeNodeParamsWithDefaults(perNodeConf.withFallback(defaultConf))
146163
val perNodeFeatures = nodeParams.featuresFor(PublicKey(ByteVector.fromValidHex("02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
147-
assert(perNodeFeatures === Features(VariableLengthOnion -> Optional, PaymentSecret -> Mandatory, BasicMultiPartPayment -> Mandatory))
164+
assert(perNodeFeatures === Features(VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory, BasicMultiPartPayment -> Mandatory))
148165
}
149166

150167
test("override feerate mismatch tolerance") {

eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package fr.acinq.eclair
1818

1919
import fr.acinq.bitcoin.Crypto.PrivateKey
2020
import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi, SatoshiLong, Script}
21-
import fr.acinq.eclair.FeatureSupport.Optional
21+
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
2222
import fr.acinq.eclair.Features._
2323
import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, FeeratesPerKw, OnChainFeeConf, _}
2424
import fr.acinq.eclair.channel.LocalParams
@@ -88,8 +88,8 @@ object TestConstants {
8888
OptionDataLossProtect -> Optional,
8989
ChannelRangeQueries -> Optional,
9090
ChannelRangeQueriesExtended -> Optional,
91-
VariableLengthOnion -> Optional,
92-
PaymentSecret -> Optional,
91+
VariableLengthOnion -> Mandatory,
92+
PaymentSecret -> Mandatory,
9393
BasicMultiPartPayment -> Optional
9494
),
9595
Set(UnknownFeature(TestFeature.optional))
@@ -194,8 +194,8 @@ object TestConstants {
194194
OptionDataLossProtect -> Optional,
195195
ChannelRangeQueries -> Optional,
196196
ChannelRangeQueriesExtended -> Optional,
197-
VariableLengthOnion -> Optional,
198-
PaymentSecret -> Optional,
197+
VariableLengthOnion -> Mandatory,
198+
PaymentSecret -> Mandatory,
199199
BasicMultiPartPayment -> Optional
200200
),
201201
pluginParams = Nil,

eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ abstract class IntegrationSpec extends TestKitBaseClass with BitcoindService wit
8686
s"eclair.features.${OptionDataLossProtect.rfcName}" -> "optional",
8787
s"eclair.features.${ChannelRangeQueries.rfcName}" -> "optional",
8888
s"eclair.features.${ChannelRangeQueriesExtended.rfcName}" -> "optional",
89-
s"eclair.features.${VariableLengthOnion.rfcName}" -> "optional",
90-
s"eclair.features.${PaymentSecret.rfcName}" -> "optional",
89+
s"eclair.features.${VariableLengthOnion.rfcName}" -> "mandatory",
90+
s"eclair.features.${PaymentSecret.rfcName}" -> "mandatory",
9191
s"eclair.features.${BasicMultiPartPayment.rfcName}" -> "optional"
9292
).asJava)
9393

eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import akka.actor.PoisonPill
2020
import akka.testkit.{TestFSMRef, TestProbe}
2121
import fr.acinq.bitcoin.Block
2222
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
23-
import fr.acinq.eclair.FeatureSupport.Optional
24-
import fr.acinq.eclair.Features.{BasicMultiPartPayment, ChannelRangeQueries, VariableLengthOnion}
23+
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
24+
import fr.acinq.eclair.Features.{BasicMultiPartPayment, ChannelRangeQueries, PaymentSecret, VariableLengthOnion}
2525
import fr.acinq.eclair.TestConstants._
2626
import fr.acinq.eclair._
2727
import fr.acinq.eclair.crypto.TransportHandler
@@ -190,7 +190,7 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi
190190

191191
test("sync when requested") { f =>
192192
import f._
193-
val remoteInit = protocol.Init(Features(ChannelRangeQueries -> Optional))
193+
val remoteInit = protocol.Init(Features(ChannelRangeQueries -> Optional, VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory))
194194
connect(nodeParams, remoteNodeId, switchboard, router, connection, transport, peerConnection, peer, remoteInit, doSync = true)
195195
}
196196

eclair-core/src/test/scala/fr/acinq/eclair/payment/MultiPartHandlerSpec.scala

+7-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import akka.actor.ActorRef
2020
import akka.actor.Status.Failure
2121
import akka.testkit.{TestActorRef, TestProbe}
2222
import fr.acinq.bitcoin.{ByteVector32, Crypto}
23-
import fr.acinq.eclair.FeatureSupport.Optional
23+
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
2424
import fr.acinq.eclair.Features._
2525
import fr.acinq.eclair.TestConstants.Alice
2626
import fr.acinq.eclair.channel.{CMD_FAIL_HTLC, CMD_FULFILL_HTLC, Register}
@@ -45,18 +45,19 @@ import scala.concurrent.duration._
4545
class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
4646

4747
val featuresWithoutMpp = Features(
48-
VariableLengthOnion -> Optional,
49-
PaymentSecret -> Optional,
48+
VariableLengthOnion -> Mandatory,
49+
PaymentSecret -> Mandatory,
5050
)
5151

5252
val featuresWithMpp = Features(
53-
VariableLengthOnion -> Optional,
54-
PaymentSecret -> Optional,
53+
VariableLengthOnion -> Mandatory,
54+
PaymentSecret -> Mandatory,
5555
BasicMultiPartPayment -> Optional
5656
)
5757

5858
val featuresWithKeySend = Features(
59-
VariableLengthOnion -> Optional,
59+
VariableLengthOnion -> Mandatory,
60+
PaymentSecret -> Mandatory,
6061
KeySend -> Optional
6162
)
6263

eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentInitiatorSpec.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package fr.acinq.eclair.payment
1919
import akka.actor.{ActorContext, ActorRef}
2020
import akka.testkit.{TestActorRef, TestProbe}
2121
import fr.acinq.bitcoin.Block
22-
import fr.acinq.eclair.FeatureSupport.Optional
22+
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
2323
import fr.acinq.eclair.Features._
2424
import fr.acinq.eclair.UInt64.Conversions._
2525
import fr.acinq.eclair.channel.Channel
@@ -53,13 +53,13 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
5353
case class FixtureParam(nodeParams: NodeParams, initiator: TestActorRef[PaymentInitiator], payFsm: TestProbe, multiPartPayFsm: TestProbe, sender: TestProbe, eventListener: TestProbe)
5454

5555
val featuresWithoutMpp = Features(
56-
VariableLengthOnion -> Optional,
57-
PaymentSecret -> Optional
56+
VariableLengthOnion -> Mandatory,
57+
PaymentSecret -> Mandatory
5858
)
5959

6060
val featuresWithMpp = Features(
61-
VariableLengthOnion -> Optional,
62-
PaymentSecret -> Optional,
61+
VariableLengthOnion -> Mandatory,
62+
PaymentSecret -> Mandatory,
6363
BasicMultiPartPayment -> Optional,
6464
)
6565

@@ -143,7 +143,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
143143
test("forward single-part payment when multi-part deactivated", Tag("mpp_disabled")) { f =>
144144
import f._
145145
val finalExpiryDelta = CltvExpiryDelta(24)
146-
val pr = PaymentRequest(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, randomKey, "Some MPP invoice", finalExpiryDelta, features = Some(PaymentRequestFeatures(VariableLengthOnion.optional, PaymentSecret.optional, BasicMultiPartPayment.optional)))
146+
val pr = PaymentRequest(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, randomKey, "Some MPP invoice", finalExpiryDelta, features = Some(PaymentRequestFeatures(VariableLengthOnion.mandatory, PaymentSecret.mandatory, BasicMultiPartPayment.optional)))
147147
val req = SendPaymentRequest(finalAmount, paymentHash, c, 1, /* ignored since the invoice provides it */ CltvExpiryDelta(12), Some(pr))
148148
assert(req.finalExpiry(nodeParams.currentBlockHeight) === (finalExpiryDelta + 1).toCltvExpiry(nodeParams.currentBlockHeight))
149149
sender.send(initiator, req)
@@ -154,7 +154,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
154154

155155
test("forward multi-part payment") { f =>
156156
import f._
157-
val pr = PaymentRequest(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, randomKey, "Some invoice", CltvExpiryDelta(18), features = Some(PaymentRequestFeatures(VariableLengthOnion.optional, PaymentSecret.optional, BasicMultiPartPayment.optional)))
157+
val pr = PaymentRequest(Block.LivenetGenesisBlock.hash, Some(finalAmount), paymentHash, randomKey, "Some invoice", CltvExpiryDelta(18), features = Some(PaymentRequestFeatures(VariableLengthOnion.mandatory, PaymentSecret.mandatory, BasicMultiPartPayment.optional)))
158158
val req = SendPaymentRequest(finalAmount + 100.msat, paymentHash, c, 1, CltvExpiryDelta(42), Some(pr))
159159
sender.send(initiator, req)
160160
val id = sender.expectMsgType[UUID]
@@ -245,7 +245,7 @@ class PaymentInitiatorSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
245245
assert(trampolinePayload.outgoingCltv.toLong === currentBlockCount + 9 + 1)
246246
assert(trampolinePayload.outgoingNodeId === c)
247247
assert(trampolinePayload.paymentSecret === pr.paymentSecret)
248-
assert(trampolinePayload.invoiceFeatures === Some(hex"8200")) // var_onion_optin, payment_secret
248+
assert(trampolinePayload.invoiceFeatures === Some(hex"4100")) // var_onion_optin, payment_secret
249249
}
250250

251251
test("reject trampoline to legacy payment for 0-value invoice") { f =>

eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentPacketSpec.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
216216
// a -> b -> c d -> e
217217

218218
val routingHints = List(List(PaymentRequest.ExtraHop(randomKey.publicKey, ShortChannelId(42), 10 msat, 100, CltvExpiryDelta(144))))
219-
val invoiceFeatures = PaymentRequestFeatures(VariableLengthOnion.optional, PaymentSecret.optional, BasicMultiPartPayment.optional)
219+
val invoiceFeatures = PaymentRequestFeatures(VariableLengthOnion.mandatory, PaymentSecret.mandatory, BasicMultiPartPayment.optional)
220220
val invoice = PaymentRequest(Block.RegtestGenesisBlock.hash, Some(finalAmount), paymentHash, priv_a.privateKey, "#reckless", CltvExpiryDelta(18), None, None, routingHints, features = Some(invoiceFeatures))
221221
val (amount_ac, expiry_ac, trampolineOnion) = buildTrampolineToLegacyPacket(invoice, trampolineHops, FinalLegacyPayload(finalAmount, finalExpiry))
222222
assert(amount_ac === amount_bc)
@@ -257,7 +257,7 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
257257
assert(inner_d.outgoingNodeId === e)
258258
assert(inner_d.totalAmount === finalAmount)
259259
assert(inner_d.paymentSecret === invoice.paymentSecret)
260-
assert(inner_d.invoiceFeatures === Some(hex"028200")) // var_onion_optin, payment_secret, basic_mpp
260+
assert(inner_d.invoiceFeatures === Some(hex"024100")) // var_onion_optin, payment_secret, basic_mpp
261261
assert(inner_d.invoiceRoutingInfo === Some(routingHints))
262262
}
263263

eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,8 @@ class PaymentRequestSpec extends AnyFunSuite {
431431
test("payment secret") {
432432
val pr = PaymentRequest(Block.LivenetGenesisBlock.hash, Some(123 msat), ByteVector32.One, priv, "Some invoice", CltvExpiryDelta(18))
433433
assert(pr.paymentSecret.isDefined)
434-
assert(pr.features === PaymentRequestFeatures(PaymentSecret.optional, VariableLengthOnion.optional))
435-
assert(!pr.features.requirePaymentSecret)
434+
assert(pr.features === PaymentRequestFeatures(PaymentSecret.mandatory, VariableLengthOnion.mandatory))
435+
assert(pr.features.requirePaymentSecret)
436436

437437
val pr1 = PaymentRequest.read(PaymentRequest.write(pr))
438438
assert(pr1.paymentSecret === pr.paymentSecret)

eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/NodeRelayerSpec.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ class NodeRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("appl
575575

576576
// Receive an upstream multi-part payment.
577577
val hints = List(List(ExtraHop(outgoingNodeId, ShortChannelId(42), feeBase = 10 msat, feeProportionalMillionths = 1, cltvExpiryDelta = CltvExpiryDelta(12))))
578-
val features = PaymentRequestFeatures(VariableLengthOnion.optional, PaymentSecret.mandatory, BasicMultiPartPayment.optional)
578+
val features = PaymentRequestFeatures(VariableLengthOnion.mandatory, PaymentSecret.mandatory, BasicMultiPartPayment.optional)
579579
val pr = PaymentRequest(Block.LivenetGenesisBlock.hash, Some(outgoingAmount * 3), paymentHash, randomKey, "Some invoice", CltvExpiryDelta(18), extraHops = hints, features = Some(features))
580580
val incomingPayments = incomingMultiPart.map(incoming => incoming.copy(innerPayload = Onion.createNodeRelayToNonTrampolinePayload(
581581
incoming.innerPayload.amountToForward, outgoingAmount * 3, outgoingExpiry, outgoingNodeId, pr

eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class AnnouncementsSpec extends AnyFunSuite {
5353

5454
test("create valid signed node announcement") {
5555
val ann = makeNodeAnnouncement(Alice.nodeParams.privateKey, Alice.nodeParams.alias, Alice.nodeParams.color, Alice.nodeParams.publicAddresses, Alice.nodeParams.features)
56-
assert(ann.features.hasFeature(Features.VariableLengthOnion, Some(FeatureSupport.Optional)))
56+
assert(ann.features.hasFeature(Features.VariableLengthOnion, Some(FeatureSupport.Mandatory)))
5757
assert(checkSig(ann))
5858
assert(checkSig(ann.copy(timestamp = 153)) === false)
5959
}

0 commit comments

Comments
 (0)