mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-21 22:11:46 +01:00
Allow plugins to set a dual funding contribution (#2829)
We support dual funding, but we cannot decide for the node operator when to contribute to channels that are being opened to us. Node operators may want very different policies depending on their goals. Instead of trying to figure out a general set of policies, we let plugins decide when to contribute to channels and how much. Node operators are thus free to implement whatever logic makes sense for them in a plugin.
This commit is contained in:
parent
36a3c8897c
commit
fd0cdf6fc1
5 changed files with 17 additions and 16 deletions
|
@ -67,7 +67,7 @@ case class InterceptOpenChannelReceived(replyTo: ActorRef[InterceptOpenChannelRe
|
|||
}
|
||||
|
||||
sealed trait InterceptOpenChannelResponse
|
||||
case class AcceptOpenChannel(temporaryChannelId: ByteVector32, defaultParams: DefaultParams) extends InterceptOpenChannelResponse
|
||||
case class AcceptOpenChannel(temporaryChannelId: ByteVector32, defaultParams: DefaultParams, localFundingAmount_opt: Option[Satoshi]) extends InterceptOpenChannelResponse
|
||||
case class RejectOpenChannel(temporaryChannelId: ByteVector32, error: Error) extends InterceptOpenChannelResponse
|
||||
// @formatter:on
|
||||
|
||||
|
|
|
@ -167,7 +167,8 @@ private class OpenChannelInterceptor(peer: ActorRef[Any],
|
|||
nodeParams.pluginOpenChannelInterceptor match {
|
||||
case Some(plugin) => queryPlugin(plugin, request, localParams, ChannelConfig.standard, channelType)
|
||||
case None =>
|
||||
peer ! SpawnChannelNonInitiator(request.open, ChannelConfig.standard, channelType, localParams, request.peerConnection.toClassic)
|
||||
// NB: we don't add a contribution to the funding amount.
|
||||
peer ! SpawnChannelNonInitiator(request.open, ChannelConfig.standard, channelType, localParams, None, request.peerConnection.toClassic)
|
||||
waitForRequest()
|
||||
}
|
||||
case PendingChannelsRateLimiterResponse(PendingChannelsRateLimiter.ChannelRateLimited) =>
|
||||
|
@ -186,7 +187,7 @@ private class OpenChannelInterceptor(peer: ActorRef[Any],
|
|||
receiveCommandMessage[QueryPluginCommands](context, "queryPlugin") {
|
||||
case PluginOpenChannelResponse(pluginResponse: AcceptOpenChannel) =>
|
||||
val localParams1 = updateLocalParams(localParams, pluginResponse.defaultParams)
|
||||
peer ! SpawnChannelNonInitiator(request.open, channelConfig, channelType, localParams1, request.peerConnection.toClassic)
|
||||
peer ! SpawnChannelNonInitiator(request.open, channelConfig, channelType, localParams1, pluginResponse.localFundingAmount_opt, request.peerConnection.toClassic)
|
||||
timers.cancel(PluginTimeout)
|
||||
waitForRequest()
|
||||
case PluginOpenChannelResponse(pluginResponse: RejectOpenChannel) =>
|
||||
|
|
|
@ -199,7 +199,7 @@ class Peer(val nodeParams: NodeParams,
|
|||
stay()
|
||||
}
|
||||
|
||||
case Event(SpawnChannelNonInitiator(open, channelConfig, channelType, localParams, peerConnection), d: ConnectedData) =>
|
||||
case Event(SpawnChannelNonInitiator(open, channelConfig, channelType, localParams, localFundingAmount_opt, peerConnection), d: ConnectedData) =>
|
||||
val temporaryChannelId = open.fold(_.temporaryChannelId, _.temporaryChannelId)
|
||||
if (peerConnection == d.peerConnection) {
|
||||
val channel = spawnChannel()
|
||||
|
@ -209,8 +209,7 @@ class Peer(val nodeParams: NodeParams,
|
|||
channel ! INPUT_INIT_CHANNEL_NON_INITIATOR(open.temporaryChannelId, None, dualFunded = false, None, localParams, d.peerConnection, d.remoteInit, channelConfig, channelType)
|
||||
channel ! open
|
||||
case Right(open) =>
|
||||
// NB: we don't add a contribution to the funding amount.
|
||||
channel ! INPUT_INIT_CHANNEL_NON_INITIATOR(open.temporaryChannelId, None, dualFunded = true, None, localParams, d.peerConnection, d.remoteInit, channelConfig, channelType)
|
||||
channel ! INPUT_INIT_CHANNEL_NON_INITIATOR(open.temporaryChannelId, localFundingAmount_opt, dualFunded = true, None, localParams, d.peerConnection, d.remoteInit, channelConfig, channelType)
|
||||
channel ! open
|
||||
}
|
||||
stay() using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel))
|
||||
|
@ -545,7 +544,7 @@ object Peer {
|
|||
}
|
||||
|
||||
case class SpawnChannelInitiator(replyTo: akka.actor.typed.ActorRef[OpenChannelResponse], cmd: Peer.OpenChannel, channelConfig: ChannelConfig, channelType: SupportedChannelType, localParams: LocalParams)
|
||||
case class SpawnChannelNonInitiator(open: Either[protocol.OpenChannel, protocol.OpenDualFundedChannel], channelConfig: ChannelConfig, channelType: SupportedChannelType, localParams: LocalParams, peerConnection: ActorRef)
|
||||
case class SpawnChannelNonInitiator(open: Either[protocol.OpenChannel, protocol.OpenDualFundedChannel], channelConfig: ChannelConfig, channelType: SupportedChannelType, localParams: LocalParams, localFundingAmount_opt: Option[Satoshi], peerConnection: ActorRef)
|
||||
|
||||
case class GetPeerInfo(replyTo: Option[typed.ActorRef[PeerInfoResponse]])
|
||||
sealed trait PeerInfoResponse { def nodeId: PublicKey }
|
||||
|
|
|
@ -87,13 +87,14 @@ class OpenChannelInterceptorSpec extends ScalaTestWithActorTestKit(ConfigFactory
|
|||
val openChannelNonInitiator = OpenChannelNonInitiator(remoteNodeId, Left(openChannel), Features.empty, Features.empty, peerConnection.ref, remoteAddress)
|
||||
openChannelInterceptor ! openChannelNonInitiator
|
||||
pendingChannelsRateLimiter.expectMessageType[AddOrRejectChannel].replyTo ! PendingChannelsRateLimiter.AcceptOpenChannel
|
||||
pluginInterceptor.expectMessageType[InterceptOpenChannelReceived].replyTo ! AcceptOpenChannel(randomBytes32(), defaultParams)
|
||||
val updatedLocalParams = peer.expectMessageType[SpawnChannelNonInitiator].localParams
|
||||
assert(updatedLocalParams.dustLimit == defaultParams.dustLimit)
|
||||
assert(updatedLocalParams.htlcMinimum == defaultParams.htlcMinimum)
|
||||
assert(updatedLocalParams.maxAcceptedHtlcs == defaultParams.maxAcceptedHtlcs)
|
||||
assert(updatedLocalParams.maxHtlcValueInFlightMsat == defaultParams.maxHtlcValueInFlightMsat)
|
||||
assert(updatedLocalParams.toSelfDelay == defaultParams.toSelfDelay)
|
||||
pluginInterceptor.expectMessageType[InterceptOpenChannelReceived].replyTo ! AcceptOpenChannel(randomBytes32(), defaultParams, Some(50_000 sat))
|
||||
val response = peer.expectMessageType[SpawnChannelNonInitiator]
|
||||
assert(response.localFundingAmount_opt.contains(50_000 sat))
|
||||
assert(response.localParams.dustLimit == defaultParams.dustLimit)
|
||||
assert(response.localParams.htlcMinimum == defaultParams.htlcMinimum)
|
||||
assert(response.localParams.maxAcceptedHtlcs == defaultParams.maxAcceptedHtlcs)
|
||||
assert(response.localParams.maxHtlcValueInFlightMsat == defaultParams.maxHtlcValueInFlightMsat)
|
||||
assert(response.localParams.toSelfDelay == defaultParams.toSelfDelay)
|
||||
}
|
||||
|
||||
test("continue channel open if no interceptor plugin registered and pending channels rate limiter accepts it") { f =>
|
||||
|
@ -146,7 +147,7 @@ class OpenChannelInterceptorSpec extends ScalaTestWithActorTestKit(ConfigFactory
|
|||
assert(peer.expectMessageType[OutgoingMessage].msg.asInstanceOf[Error].channelId == ByteVector32.One)
|
||||
|
||||
// original request accepted after plugin accepts it
|
||||
pluginInterceptor.expectMessageType[InterceptOpenChannelReceived].replyTo ! AcceptOpenChannel(randomBytes32(), defaultParams)
|
||||
pluginInterceptor.expectMessageType[InterceptOpenChannelReceived].replyTo ! AcceptOpenChannel(randomBytes32(), defaultParams, None)
|
||||
assert(peer.expectMessageType[SpawnChannelNonInitiator].open == Left(openChannel))
|
||||
eventListener.expectMessageType[ChannelAborted]
|
||||
}
|
||||
|
|
|
@ -629,7 +629,7 @@ class PeerSpec extends FixtureSpec {
|
|||
val open = createOpenChannelMessage()
|
||||
system.eventStream.subscribe(probe.ref, classOf[ChannelAborted])
|
||||
connect(remoteNodeId, peer, peerConnection, switchboard)
|
||||
peer ! SpawnChannelNonInitiator(Left(open), ChannelConfig.standard, ChannelTypes.Standard(), localParams, ActorRef.noSender)
|
||||
peer ! SpawnChannelNonInitiator(Left(open), ChannelConfig.standard, ChannelTypes.Standard(), localParams, None, ActorRef.noSender)
|
||||
val channelAborted = probe.expectMsgType[ChannelAborted]
|
||||
assert(channelAborted.remoteNodeId == remoteNodeId)
|
||||
assert(channelAborted.channelId == open.temporaryChannelId)
|
||||
|
|
Loading…
Add table
Reference in a new issue