@@ -22,7 +22,7 @@ import akka.actor.typed.scaladsl.adapter.TypedActorRefOps
22
22
import akka .actor .typed .scaladsl .{ActorContext , Behaviors }
23
23
import akka .actor .typed .{ActorRef , Behavior }
24
24
import fr .acinq .bitcoin .scalacompat .Crypto .PublicKey
25
- import fr .acinq .bitcoin .scalacompat .{BtcDouble , ByteVector32 , Satoshi , Script }
25
+ import fr .acinq .bitcoin .scalacompat .{BtcDouble , ByteVector32 , Satoshi , SatoshiLong , Script }
26
26
import fr .acinq .eclair .Features .Wumbo
27
27
import fr .acinq .eclair .blockchain .OnchainPubkeyCache
28
28
import fr .acinq .eclair .channel ._
@@ -84,8 +84,8 @@ object OpenChannelInterceptor {
84
84
}
85
85
}
86
86
87
- def makeChannelParams (nodeParams : NodeParams , initFeatures : Features [ InitFeature ], upfrontShutdownScript_opt : Option [ ByteVector ], walletStaticPaymentBasepoint_opt : Option [ PublicKey ], isChannelOpener : Boolean , paysCommitTxFees : Boolean , dualFunded : Boolean , fundingAmount : Satoshi , unlimitedMaxHtlcValueInFlight : Boolean ): LocalParams = {
88
- val maxHtlcValueInFlightMsat = if (unlimitedMaxHtlcValueInFlight) {
87
+ private def computeMaxHtlcValueInFlight (nodeParams : NodeParams , fundingAmount : Satoshi , unlimitedMaxHtlcValueInFlight : Boolean ): MilliSatoshi = {
88
+ if (unlimitedMaxHtlcValueInFlight) {
89
89
// We don't want to impose limits on the amount in flight, typically to allow fully emptying the channel.
90
90
21e6 .btc.toMilliSatoshi
91
91
} else {
@@ -94,11 +94,14 @@ object OpenChannelInterceptor {
94
94
// base it on the amount that we're contributing instead of the total funding amount.
95
95
nodeParams.channelConf.maxHtlcValueInFlightMsat.min(fundingAmount * nodeParams.channelConf.maxHtlcValueInFlightPercent / 100 )
96
96
}
97
+ }
98
+
99
+ def makeChannelParams (nodeParams : NodeParams , initFeatures : Features [InitFeature ], upfrontShutdownScript_opt : Option [ByteVector ], walletStaticPaymentBasepoint_opt : Option [PublicKey ], isChannelOpener : Boolean , paysCommitTxFees : Boolean , dualFunded : Boolean , fundingAmount : Satoshi , unlimitedMaxHtlcValueInFlight : Boolean ): LocalParams = {
97
100
LocalParams (
98
101
nodeParams.nodeId,
99
102
nodeParams.channelKeyManager.newFundingKeyPath(isChannelOpener), // we make sure that opener and non-opener key paths end differently
100
103
dustLimit = nodeParams.channelConf.dustLimit,
101
- maxHtlcValueInFlightMsat = maxHtlcValueInFlightMsat ,
104
+ maxHtlcValueInFlightMsat = computeMaxHtlcValueInFlight(nodeParams, fundingAmount, unlimitedMaxHtlcValueInFlight) ,
102
105
initialRequestedChannelReserve_opt = if (dualFunded) None else Some ((fundingAmount * nodeParams.channelConf.reserveToFundingRatio).max(nodeParams.channelConf.dustLimit)), // BOLT #2: make sure that our reserve is above our dust limit
103
106
htlcMinimum = nodeParams.channelConf.htlcMinimum,
104
107
toSelfDelay = nodeParams.channelConf.toRemoteDelay, // we choose their delay
@@ -142,7 +145,9 @@ private class OpenChannelInterceptor(peer: ActorRef[Any],
142
145
val channelType = request.open.channelType_opt.getOrElse(ChannelTypes .defaultFromFeatures(request.localFeatures, request.remoteFeatures, channelFlags.announceChannel))
143
146
val dualFunded = Features .canUseFeature(request.localFeatures, request.remoteFeatures, Features .DualFunding )
144
147
val upfrontShutdownScript = Features .canUseFeature(request.localFeatures, request.remoteFeatures, Features .UpfrontShutdownScript )
145
- val localParams = createLocalParams(nodeParams, request.localFeatures, upfrontShutdownScript, channelType, isChannelOpener = true , paysCommitTxFees = true , dualFunded = dualFunded, request.open.fundingAmount, request.open.disableMaxHtlcValueInFlight)
148
+ // If we're purchasing liquidity, we expect our peer to contribute at least the amount we're purchasing, otherwise we'll cancel the funding attempt.
149
+ val expectedFundingAmount = request.open.fundingAmount + request.open.requestFunding_opt.map(_.requestedAmount).getOrElse(0 sat)
150
+ val localParams = createLocalParams(nodeParams, request.localFeatures, upfrontShutdownScript, channelType, isChannelOpener = true , paysCommitTxFees = true , dualFunded = dualFunded, expectedFundingAmount, request.open.disableMaxHtlcValueInFlight)
146
151
peer ! Peer .SpawnChannelInitiator (request.replyTo, request.open, ChannelConfig .standard, channelType, localParams)
147
152
waitForRequest()
148
153
}
@@ -210,7 +215,10 @@ private class OpenChannelInterceptor(peer: ActorRef[Any],
210
215
request.open.fold(_ => None , _.requestFunding_opt) match {
211
216
case Some (requestFunding) if Features .canUseFeature(request.localFeatures, request.remoteFeatures, Features .OnTheFlyFunding ) && localParams.paysCommitTxFees =>
212
217
val addFunding = LiquidityAds .AddFunding (requestFunding.requestedAmount, nodeParams.willFundRates_opt)
213
- val accept = SpawnChannelNonInitiator (request.open, ChannelConfig .standard, channelType, Some (addFunding), localParams, request.peerConnection.toClassic)
218
+ // Now that we know how much we'll contribute to the funding transaction, we update the maxHtlcValueInFlight.
219
+ val maxHtlcValueInFlight = localParams.maxHtlcValueInFlightMsat.max(computeMaxHtlcValueInFlight(nodeParams, request.fundingAmount + addFunding.fundingAmount, unlimitedMaxHtlcValueInFlight = false ))
220
+ val localParams1 = localParams.copy(maxHtlcValueInFlightMsat = maxHtlcValueInFlight)
221
+ val accept = SpawnChannelNonInitiator (request.open, ChannelConfig .standard, channelType, Some (addFunding), localParams1, request.peerConnection.toClassic)
214
222
checkNoExistingChannel(request, accept)
215
223
case _ =>
216
224
// We don't honor liquidity ads for new channels: node operators should use plugin for that.
0 commit comments