Skip to content

Commit 2dfdc26

Browse files
authored
Make option_channel_type mandatory (#3046)
This is the first step before we make the change to always assume option_channel_type. see lightning/bolts#1232 Removed tests that depend on a node without the option_channel_type feature set. Updated some tests that must explicitly set the init features to include option_channel_type.
1 parent b4e938d commit 2dfdc26

File tree

8 files changed

+24
-40
lines changed

8 files changed

+24
-40
lines changed

docs/release-notes/eclair-vnext.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

1111
- `listoffers` now returns more details about each offer.
1212

13+
14+
### Configuration changes
15+
16+
- The default for `eclair.features.option_channel_type` is now `mandatory` instead of `optional`. This change prepares nodes to always assume the behavior of `option_channel_type` from peers when Bolts PR [#1232](https://github.com/lightning/bolts/pull/1232) is adopted. Until [#1232](https://github.com/lightning/bolts/pull/1232) is adopted you can still set `option_channel_type` to `optional` in your `eclair.conf` file for specific peers that do not yet support this option, see `Configure.md` for more information.
17+
1318
### Miscellaneous improvements and bug fixes
1419

1520
#### Remove confirmation scaling based on funding amount

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ eclair {
7676
// This feature should only be enabled when acting as an LSP for mobile wallets.
7777
// When activating this feature, the peer-storage section should be customized to match desired SLAs.
7878
option_provide_storage = disabled
79-
option_channel_type = optional
79+
option_channel_type = mandatory
8080
option_scid_alias = optional
8181
option_payment_metadata = optional
8282
// By enabling option_zeroconf, you will be trusting your peer as fundee. You will lose funds if they double spend

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ object TestConstants {
110110
Features.Quiescence -> FeatureSupport.Optional,
111111
Features.SplicePrototype -> FeatureSupport.Optional,
112112
Features.ProvideStorage -> FeatureSupport.Optional,
113+
Features.ChannelType -> FeatureSupport.Mandatory
113114
),
114115
unknown = Set(UnknownFeature(TestFeature.optional))
115116
),
@@ -295,6 +296,7 @@ object TestConstants {
295296
Features.AnchorOutputsZeroFeeHtlcTx -> FeatureSupport.Optional,
296297
Features.Quiescence -> FeatureSupport.Optional,
297298
Features.SplicePrototype -> FeatureSupport.Optional,
299+
Features.ChannelType -> FeatureSupport.Mandatory
298300
),
299301
pluginParams = Nil,
300302
overrideInitFeatures = Map.empty,

eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ object ChannelStateTestsTags {
8282
val HighDustLimitDifferenceBobAlice = "high_dust_limit_difference_bob_alice"
8383
/** If set, Alice and Bob will use a very large tolerance for feerate mismatch. */
8484
val HighFeerateMismatchTolerance = "high_feerate_mismatch_tolerance"
85-
/** If set, channels will use option_channel_type. */
86-
val ChannelType = "option_channel_type"
8785
/** If set, channels will use option_zeroconf. */
8886
val ZeroConf = "zeroconf"
8987
/** If set, channels will use option_scid_alias. */
@@ -190,7 +188,6 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
190188
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs))(_.updated(Features.StaticRemoteKey, FeatureSupport.Optional).updated(Features.AnchorOutputs, FeatureSupport.Optional).updated(Features.AnchorOutputsZeroFeeHtlcTx, FeatureSupport.Optional))
191189
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ShutdownAnySegwit))(_.updated(Features.ShutdownAnySegwit, FeatureSupport.Optional))
192190
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.UpfrontShutdownScript))(_.updated(Features.UpfrontShutdownScript, FeatureSupport.Optional))
193-
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ChannelType))(_.updated(Features.ChannelType, FeatureSupport.Optional))
194191
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ZeroConf))(_.updated(Features.ZeroConf, FeatureSupport.Optional))
195192
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ScidAlias))(_.updated(Features.ScidAlias, FeatureSupport.Optional))
196193
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DualFunding))(_.updated(Features.DualFunding, FeatureSupport.Optional))
@@ -202,7 +199,6 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
202199
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs))(_.updated(Features.StaticRemoteKey, FeatureSupport.Optional).updated(Features.AnchorOutputs, FeatureSupport.Optional).updated(Features.AnchorOutputsZeroFeeHtlcTx, FeatureSupport.Optional))
203200
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ShutdownAnySegwit))(_.updated(Features.ShutdownAnySegwit, FeatureSupport.Optional))
204201
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.UpfrontShutdownScript))(_.updated(Features.UpfrontShutdownScript, FeatureSupport.Optional))
205-
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ChannelType))(_.updated(Features.ChannelType, FeatureSupport.Optional))
206202
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ZeroConf))(_.updated(Features.ZeroConf, FeatureSupport.Optional))
207203
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ScidAlias))(_.updated(Features.ScidAlias, FeatureSupport.Optional))
208204
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DualFunding))(_.updated(Features.DualFunding, FeatureSupport.Optional))

eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package fr.acinq.eclair.channel.states.a
1818

1919
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
2020
import akka.testkit.{TestFSMRef, TestProbe}
21+
import com.softwaremill.quicklens.ModifyPimp
2122
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong}
2223
import fr.acinq.eclair.TestConstants.{Alice, Bob}
2324
import fr.acinq.eclair.blockchain.NoOpOnChainWallet
@@ -114,19 +115,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
114115
aliceOpenReplyTo.expectNoMessage()
115116
}
116117

117-
test("recv AcceptChannel (channel type not set)", Tag(ChannelStateTestsTags.AnchorOutputs)) { f =>
118-
import f._
119-
val accept = bob2alice.expectMsgType[AcceptChannel]
120-
assert(accept.channelType_opt.contains(ChannelTypes.AnchorOutputs()))
121-
// Alice explicitly asked for an anchor output channel. Bob doesn't support explicit channel type negotiation but
122-
// they both activated anchor outputs so it is the default choice anyway.
123-
bob2alice.forward(alice, accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty))))
124-
awaitCond(alice.stateName == WAIT_FOR_FUNDING_INTERNAL)
125-
assert(alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_INTERNAL].params.commitmentFormat == UnsafeLegacyAnchorOutputsCommitmentFormat)
126-
aliceOpenReplyTo.expectNoMessage()
127-
}
128-
129-
test("recv AcceptChannel (channel type not set but feature bit set)", Tag(ChannelStateTestsTags.ChannelType), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
118+
test("recv AcceptChannel (channel type not set but feature bit set)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
130119
import f._
131120
val accept = bob2alice.expectMsgType[AcceptChannel]
132121
assert(accept.channelType_opt.contains(ChannelTypes.AnchorOutputsZeroFeeHtlcTx()))
@@ -148,19 +137,6 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
148137
aliceOpenReplyTo.expectNoMessage()
149138
}
150139

151-
test("recv AcceptChannel (non-default channel type not set)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs), Tag(StandardChannelType)) { f =>
152-
import f._
153-
val accept = bob2alice.expectMsgType[AcceptChannel]
154-
assert(accept.channelType_opt.contains(ChannelTypes.Standard()))
155-
// Alice asked for a standard channel whereas they both support anchor outputs. Bob doesn't support explicit channel
156-
// type negotiation so Alice needs to abort because the channel types won't match.
157-
bob2alice.forward(alice, accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty))))
158-
alice2bob.expectMsg(Error(accept.temporaryChannelId, "invalid channel_type=anchor_outputs_zero_fee_htlc_tx, expected channel_type=standard"))
159-
listener.expectMsgType[ChannelAborted]
160-
awaitCond(alice.stateName == CLOSED)
161-
aliceOpenReplyTo.expectMsgType[OpenChannelResponse.Rejected]
162-
}
163-
164140
test("recv AcceptChannel (anchor outputs channel type without enabling the feature)") { () =>
165141
val setup = init(Alice.nodeParams, Bob.nodeParams, wallet_opt = Some(new NoOpOnChainWallet()))
166142
import setup._
@@ -169,7 +145,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
169145
val channelFlags = ChannelFlags(announceChannel = false)
170146
// Bob advertises support for anchor outputs, but Alice doesn't.
171147
val aliceParams = Alice.channelParams
172-
val bobParams = Bob.channelParams.copy(initFeatures = Features(Features.StaticRemoteKey -> FeatureSupport.Optional, Features.AnchorOutputs -> FeatureSupport.Optional))
148+
val bobParams = Bob.channelParams.copy(initFeatures = Features(Features.StaticRemoteKey -> FeatureSupport.Optional, Features.AnchorOutputs -> FeatureSupport.Optional, Features.ChannelType -> FeatureSupport.Mandatory))
173149
alice ! INPUT_INIT_CHANNEL_INITIATOR(ByteVector32.Zeroes, TestConstants.fundingSatoshis, dualFunded = false, TestConstants.anchorOutputsFeeratePerKw, TestConstants.feeratePerKw, fundingTxFeeBudget_opt = None, Some(TestConstants.initiatorPushAmount), requireConfirmedInputs = false, requestFunding_opt = None, aliceParams, alice2bob.ref, Init(bobParams.initFeatures), channelFlags, channelConfig, ChannelTypes.AnchorOutputs(), replyTo = aliceOpenReplyTo.ref.toTyped)
174150
bob ! INPUT_INIT_CHANNEL_NON_INITIATOR(ByteVector32.Zeroes, None, dualFunded = false, None, requireConfirmedInputs = false, bobParams, bob2alice.ref, Init(bobParams.initFeatures), channelConfig, ChannelTypes.AnchorOutputs())
175151
val open = alice2bob.expectMsgType[OpenChannel]
@@ -310,8 +286,11 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
310286
import f._
311287
val accept = bob2alice.expectMsgType[AcceptChannel]
312288
assert(accept.upfrontShutdownScript_opt.contains(bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CREATED].params.localParams.upfrontShutdownScript_opt.get))
313-
val accept1 = accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty)))
289+
val accept1 = accept
290+
.modify(_.tlvStream.records).using(_.filterNot(_.isInstanceOf[ChannelTlv.UpfrontShutdownScriptTlv]))
291+
.modify(_.tlvStream.records).using(_ + ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty))
314292
bob2alice.forward(alice, accept1)
293+
alice2bob.expectNoMessage(100 millis)
315294
awaitCond(alice.stateName == WAIT_FOR_FUNDING_INTERNAL)
316295
assert(alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_INTERNAL].params.remoteParams.upfrontShutdownScript_opt.isEmpty)
317296
aliceOpenReplyTo.expectNoMessage()
@@ -320,9 +299,12 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
320299
test("recv AcceptChannel (invalid upfront shutdown script)", Tag(ChannelStateTestsTags.UpfrontShutdownScript)) { f =>
321300
import f._
322301
val accept = bob2alice.expectMsgType[AcceptChannel]
323-
val accept1 = accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.fromValidHex("deadbeef"))))
302+
val accept1 = accept
303+
.modify(_.tlvStream.records).using(_.filterNot(_.isInstanceOf[ChannelTlv.UpfrontShutdownScriptTlv]))
304+
.modify(_.tlvStream.records).using(_ + ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.fromValidHex("deadbeef")))
324305
bob2alice.forward(alice, accept1)
325306
listener.expectMsgType[ChannelAborted]
307+
alice2bob.expectMsg(Error(accept.temporaryChannelId, "invalid final script"))
326308
awaitCond(alice.stateName == CLOSED)
327309
aliceOpenReplyTo.expectMsgType[OpenChannelResponse.Rejected]
328310
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ class OpenChannelInterceptorSpec extends ScalaTestWithActorTestKit(ConfigFactory
320320
}
321321
}
322322

323-
test("don't spawn a channel if channel type is missing with the feature bit set", Tag(ChannelStateTestsTags.ChannelType)) { f =>
323+
test("don't spawn a channel if channel type is missing with the feature bit set") { f =>
324324
import f._
325325

326326
val open = createOpenChannelMessage()

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
2121
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
2222
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32}
2323
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
24-
import fr.acinq.eclair.Features.{BasicMultiPartPayment, ChannelRangeQueries, PaymentSecret, StaticRemoteKey, VariableLengthOnion}
24+
import fr.acinq.eclair.Features.{BasicMultiPartPayment, ChannelRangeQueries, ChannelType, PaymentSecret, StaticRemoteKey, VariableLengthOnion}
2525
import fr.acinq.eclair.TestConstants._
2626
import fr.acinq.eclair.crypto.TransportHandler
2727
import fr.acinq.eclair.io.Peer.ConnectionDown
@@ -153,7 +153,7 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi
153153
transport.send(peerConnection, LightningMessageCodecs.initCodec.decode(hex"0000 00050100000000".bits).require.value)
154154
transport.expectMsgType[TransportHandler.ReadAck]
155155
probe.expectTerminated(transport.ref)
156-
origin.expectMsg(PeerConnection.ConnectionResult.InitializationFailed("incompatible features (unknown_32,payment_secret,var_onion_optin,option_static_remotekey)"))
156+
origin.expectMsg(PeerConnection.ConnectionResult.InitializationFailed("incompatible features (option_channel_type,option_static_remotekey,var_onion_optin,unknown_32,payment_secret)"))
157157
peer.expectMsg(ConnectionDown(peerConnection))
158158
}
159159

@@ -170,7 +170,7 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi
170170
transport.send(peerConnection, LightningMessageCodecs.initCodec.decode(hex"00050100000000 0000".bits).require.value)
171171
transport.expectMsgType[TransportHandler.ReadAck]
172172
probe.expectTerminated(transport.ref)
173-
origin.expectMsg(PeerConnection.ConnectionResult.InitializationFailed("incompatible features (unknown_32,payment_secret,var_onion_optin,option_static_remotekey)"))
173+
origin.expectMsg(PeerConnection.ConnectionResult.InitializationFailed("incompatible features (option_channel_type,option_static_remotekey,var_onion_optin,unknown_32,payment_secret)"))
174174
peer.expectMsg(ConnectionDown(peerConnection))
175175
}
176176

@@ -211,7 +211,7 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi
211211

212212
test("sync when requested") { f =>
213213
import f._
214-
val remoteInit = protocol.Init(Features(ChannelRangeQueries -> Optional, VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory, StaticRemoteKey -> Mandatory))
214+
val remoteInit = protocol.Init(Features(ChannelRangeQueries -> Optional, VariableLengthOnion -> Mandatory, PaymentSecret -> Mandatory, StaticRemoteKey -> Mandatory, ChannelType -> Mandatory))
215215
connect(nodeParams, remoteNodeId, switchboard, router, connection, transport, peerConnection, peer, remoteInit, doSync = true)
216216
}
217217

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class PeerSpec extends FixtureSpec {
7070

7171
import com.softwaremill.quicklens._
7272
val aliceParams = TestConstants.Alice.nodeParams
73-
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.ChannelType))(Features(ChannelType -> Optional))
7473
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.StaticRemoteKey))(Features(StaticRemoteKey -> Optional))
7574
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.AnchorOutputs))(Features(StaticRemoteKey -> Optional, AnchorOutputs -> Optional))
7675
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs))(Features(StaticRemoteKey -> Optional, AnchorOutputs -> Optional, AnchorOutputsZeroFeeHtlcTx -> Optional))

0 commit comments

Comments
 (0)