Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/release-notes/eclair-vnext.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

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


### Configuration changes

- 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.

### Miscellaneous improvements and bug fixes

#### Remove confirmation scaling based on funding amount
Expand Down
2 changes: 1 addition & 1 deletion eclair-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ eclair {
// This feature should only be enabled when acting as an LSP for mobile wallets.
// When activating this feature, the peer-storage section should be customized to match desired SLAs.
option_provide_storage = disabled
option_channel_type = optional
option_channel_type = mandatory
option_scid_alias = optional
option_payment_metadata = optional
// By enabling option_zeroconf, you will be trusting your peer as fundee. You will lose funds if they double spend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ object TestConstants {
Features.Quiescence -> FeatureSupport.Optional,
Features.SplicePrototype -> FeatureSupport.Optional,
Features.ProvideStorage -> FeatureSupport.Optional,
Features.ChannelType -> FeatureSupport.Mandatory
),
unknown = Set(UnknownFeature(TestFeature.optional))
),
Expand Down Expand Up @@ -295,6 +296,7 @@ object TestConstants {
Features.AnchorOutputsZeroFeeHtlcTx -> FeatureSupport.Optional,
Features.Quiescence -> FeatureSupport.Optional,
Features.SplicePrototype -> FeatureSupport.Optional,
Features.ChannelType -> FeatureSupport.Mandatory
),
pluginParams = Nil,
overrideInitFeatures = Map.empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ object ChannelStateTestsTags {
val HighDustLimitDifferenceBobAlice = "high_dust_limit_difference_bob_alice"
/** If set, Alice and Bob will use a very large tolerance for feerate mismatch. */
val HighFeerateMismatchTolerance = "high_feerate_mismatch_tolerance"
/** If set, channels will use option_channel_type. */
val ChannelType = "option_channel_type"
/** If set, channels will use option_zeroconf. */
val ZeroConf = "zeroconf"
/** If set, channels will use option_scid_alias. */
Expand Down Expand Up @@ -190,7 +188,6 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs))(_.updated(Features.StaticRemoteKey, FeatureSupport.Optional).updated(Features.AnchorOutputs, FeatureSupport.Optional).updated(Features.AnchorOutputsZeroFeeHtlcTx, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ShutdownAnySegwit))(_.updated(Features.ShutdownAnySegwit, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.UpfrontShutdownScript))(_.updated(Features.UpfrontShutdownScript, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ChannelType))(_.updated(Features.ChannelType, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ZeroConf))(_.updated(Features.ZeroConf, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ScidAlias))(_.updated(Features.ScidAlias, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DualFunding))(_.updated(Features.DualFunding, FeatureSupport.Optional))
Expand All @@ -202,7 +199,6 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs))(_.updated(Features.StaticRemoteKey, FeatureSupport.Optional).updated(Features.AnchorOutputs, FeatureSupport.Optional).updated(Features.AnchorOutputsZeroFeeHtlcTx, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ShutdownAnySegwit))(_.updated(Features.ShutdownAnySegwit, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.UpfrontShutdownScript))(_.updated(Features.UpfrontShutdownScript, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ChannelType))(_.updated(Features.ChannelType, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ZeroConf))(_.updated(Features.ZeroConf, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ScidAlias))(_.updated(Features.ScidAlias, FeatureSupport.Optional))
.modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DualFunding))(_.updated(Features.DualFunding, FeatureSupport.Optional))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package fr.acinq.eclair.channel.states.a

import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
import akka.testkit.{TestFSMRef, TestProbe}
import com.softwaremill.quicklens.ModifyPimp
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong}
import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.blockchain.NoOpOnChainWallet
Expand Down Expand Up @@ -114,19 +115,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
aliceOpenReplyTo.expectNoMessage()
}

test("recv AcceptChannel (channel type not set)", Tag(ChannelStateTestsTags.AnchorOutputs)) { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
assert(accept.channelType_opt.contains(ChannelTypes.AnchorOutputs()))
// Alice explicitly asked for an anchor output channel. Bob doesn't support explicit channel type negotiation but
// they both activated anchor outputs so it is the default choice anyway.
bob2alice.forward(alice, accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty))))
awaitCond(alice.stateName == WAIT_FOR_FUNDING_INTERNAL)
assert(alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_INTERNAL].params.commitmentFormat == UnsafeLegacyAnchorOutputsCommitmentFormat)
aliceOpenReplyTo.expectNoMessage()
}

test("recv AcceptChannel (channel type not set but feature bit set)", Tag(ChannelStateTestsTags.ChannelType), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
test("recv AcceptChannel (channel type not set but feature bit set)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
assert(accept.channelType_opt.contains(ChannelTypes.AnchorOutputsZeroFeeHtlcTx()))
Expand All @@ -148,19 +137,6 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
aliceOpenReplyTo.expectNoMessage()
}

test("recv AcceptChannel (non-default channel type not set)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs), Tag(StandardChannelType)) { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
assert(accept.channelType_opt.contains(ChannelTypes.Standard()))
// Alice asked for a standard channel whereas they both support anchor outputs. Bob doesn't support explicit channel
// type negotiation so Alice needs to abort because the channel types won't match.
bob2alice.forward(alice, accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty))))
alice2bob.expectMsg(Error(accept.temporaryChannelId, "invalid channel_type=anchor_outputs_zero_fee_htlc_tx, expected channel_type=standard"))
listener.expectMsgType[ChannelAborted]
awaitCond(alice.stateName == CLOSED)
aliceOpenReplyTo.expectMsgType[OpenChannelResponse.Rejected]
}

test("recv AcceptChannel (anchor outputs channel type without enabling the feature)") { () =>
val setup = init(Alice.nodeParams, Bob.nodeParams, wallet_opt = Some(new NoOpOnChainWallet()))
import setup._
Expand All @@ -169,7 +145,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
val channelFlags = ChannelFlags(announceChannel = false)
// Bob advertises support for anchor outputs, but Alice doesn't.
val aliceParams = Alice.channelParams
val bobParams = Bob.channelParams.copy(initFeatures = Features(Features.StaticRemoteKey -> FeatureSupport.Optional, Features.AnchorOutputs -> FeatureSupport.Optional))
val bobParams = Bob.channelParams.copy(initFeatures = Features(Features.StaticRemoteKey -> FeatureSupport.Optional, Features.AnchorOutputs -> FeatureSupport.Optional, Features.ChannelType -> FeatureSupport.Mandatory))
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)
bob ! INPUT_INIT_CHANNEL_NON_INITIATOR(ByteVector32.Zeroes, None, dualFunded = false, None, requireConfirmedInputs = false, bobParams, bob2alice.ref, Init(bobParams.initFeatures), channelConfig, ChannelTypes.AnchorOutputs())
val open = alice2bob.expectMsgType[OpenChannel]
Expand Down Expand Up @@ -310,8 +286,11 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
assert(accept.upfrontShutdownScript_opt.contains(bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CREATED].params.localParams.upfrontShutdownScript_opt.get))
val accept1 = accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty)))
val accept1 = accept
.modify(_.tlvStream.records).using(_.filterNot(_.isInstanceOf[ChannelTlv.UpfrontShutdownScriptTlv]))
.modify(_.tlvStream.records).using(_ + ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.empty))
bob2alice.forward(alice, accept1)
alice2bob.expectNoMessage(100 millis)
awaitCond(alice.stateName == WAIT_FOR_FUNDING_INTERNAL)
assert(alice.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_INTERNAL].params.remoteParams.upfrontShutdownScript_opt.isEmpty)
aliceOpenReplyTo.expectNoMessage()
Expand All @@ -320,9 +299,12 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
test("recv AcceptChannel (invalid upfront shutdown script)", Tag(ChannelStateTestsTags.UpfrontShutdownScript)) { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
val accept1 = accept.copy(tlvStream = TlvStream(ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.fromValidHex("deadbeef"))))
val accept1 = accept
.modify(_.tlvStream.records).using(_.filterNot(_.isInstanceOf[ChannelTlv.UpfrontShutdownScriptTlv]))
.modify(_.tlvStream.records).using(_ + ChannelTlv.UpfrontShutdownScriptTlv(ByteVector.fromValidHex("deadbeef")))
bob2alice.forward(alice, accept1)
listener.expectMsgType[ChannelAborted]
alice2bob.expectMsg(Error(accept.temporaryChannelId, "invalid final script"))
awaitCond(alice.stateName == CLOSED)
aliceOpenReplyTo.expectMsgType[OpenChannelResponse.Rejected]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ class OpenChannelInterceptorSpec extends ScalaTestWithActorTestKit(ConfigFactory
}
}

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

val open = createOpenChannelMessage()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32}
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
import fr.acinq.eclair.Features.{BasicMultiPartPayment, ChannelRangeQueries, PaymentSecret, StaticRemoteKey, VariableLengthOnion}
import fr.acinq.eclair.Features.{BasicMultiPartPayment, ChannelRangeQueries, ChannelType, PaymentSecret, StaticRemoteKey, VariableLengthOnion}
import fr.acinq.eclair.TestConstants._
import fr.acinq.eclair.crypto.TransportHandler
import fr.acinq.eclair.io.Peer.ConnectionDown
Expand Down Expand Up @@ -153,7 +153,7 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi
transport.send(peerConnection, LightningMessageCodecs.initCodec.decode(hex"0000 00050100000000".bits).require.value)
transport.expectMsgType[TransportHandler.ReadAck]
probe.expectTerminated(transport.ref)
origin.expectMsg(PeerConnection.ConnectionResult.InitializationFailed("incompatible features (unknown_32,payment_secret,var_onion_optin,option_static_remotekey)"))
origin.expectMsg(PeerConnection.ConnectionResult.InitializationFailed("incompatible features (option_channel_type,option_static_remotekey,var_onion_optin,unknown_32,payment_secret)"))
peer.expectMsg(ConnectionDown(peerConnection))
}

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

Expand Down Expand Up @@ -211,7 +211,7 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ class PeerSpec extends FixtureSpec {

import com.softwaremill.quicklens._
val aliceParams = TestConstants.Alice.nodeParams
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.ChannelType))(Features(ChannelType -> Optional))
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.StaticRemoteKey))(Features(StaticRemoteKey -> Optional))
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.AnchorOutputs))(Features(StaticRemoteKey -> Optional, AnchorOutputs -> Optional))
.modify(_.features).setToIf(testData.tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs))(Features(StaticRemoteKey -> Optional, AnchorOutputs -> Optional, AnchorOutputsZeroFeeHtlcTx -> Optional))
Expand Down