@@ -22,7 +22,7 @@ import akka.actor.typed.scaladsl.adapter.TypedActorRefOps
2222import akka .actor .typed .scaladsl .{ActorContext , Behaviors }
2323import akka .actor .typed .{ActorRef , Behavior }
2424import 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 }
2626import fr .acinq .eclair .Features .Wumbo
2727import fr .acinq .eclair .blockchain .OnchainPubkeyCache
2828import fr .acinq .eclair .channel ._
@@ -84,8 +84,8 @@ object OpenChannelInterceptor {
8484 }
8585 }
8686
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) {
8989 // We don't want to impose limits on the amount in flight, typically to allow fully emptying the channel.
9090 21e6 .btc.toMilliSatoshi
9191 } else {
@@ -94,11 +94,14 @@ object OpenChannelInterceptor {
9494 // base it on the amount that we're contributing instead of the total funding amount.
9595 nodeParams.channelConf.maxHtlcValueInFlightMsat.min(fundingAmount * nodeParams.channelConf.maxHtlcValueInFlightPercent / 100 )
9696 }
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 = {
97100 LocalParams (
98101 nodeParams.nodeId,
99102 nodeParams.channelKeyManager.newFundingKeyPath(isChannelOpener), // we make sure that opener and non-opener key paths end differently
100103 dustLimit = nodeParams.channelConf.dustLimit,
101- maxHtlcValueInFlightMsat = maxHtlcValueInFlightMsat ,
104+ maxHtlcValueInFlightMsat = computeMaxHtlcValueInFlight(nodeParams, fundingAmount, unlimitedMaxHtlcValueInFlight) ,
102105 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
103106 htlcMinimum = nodeParams.channelConf.htlcMinimum,
104107 toSelfDelay = nodeParams.channelConf.toRemoteDelay, // we choose their delay
@@ -142,7 +145,9 @@ private class OpenChannelInterceptor(peer: ActorRef[Any],
142145 val channelType = request.open.channelType_opt.getOrElse(ChannelTypes .defaultFromFeatures(request.localFeatures, request.remoteFeatures, channelFlags.announceChannel))
143146 val dualFunded = Features .canUseFeature(request.localFeatures, request.remoteFeatures, Features .DualFunding )
144147 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)
146151 peer ! Peer .SpawnChannelInitiator (request.replyTo, request.open, ChannelConfig .standard, channelType, localParams)
147152 waitForRequest()
148153 }
@@ -210,7 +215,10 @@ private class OpenChannelInterceptor(peer: ActorRef[Any],
210215 request.open.fold(_ => None , _.requestFunding_opt) match {
211216 case Some (requestFunding) if Features .canUseFeature(request.localFeatures, request.remoteFeatures, Features .OnTheFlyFunding ) && localParams.paysCommitTxFees =>
212217 val addFunding = LiquidityAds .AddFunding (requestFunding.requestedAmount, nodeParams.liquidityAdsConfig.rates_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)
214222 checkNoExistingChannel(request, accept)
215223 case _ =>
216224 // We don't honor liquidity ads for new channels: node operators should use plugin for that.
0 commit comments