mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-19 01:43:22 +01:00
Allow splicing on non dual-funded channels (#2727)
* Allow splicing on non dual-funded channels We currently allow splicing on top of a channel that was created without using dual funding, but it results in an incorrect channel reserve being used post-splice. * Rename requestedChannelReserve_opt This field only applies to the first funding index, so we rename it to make that explicit. Once a splice has happened, it will be ignored.
This commit is contained in:
parent
70d150bff6
commit
d274fc1939
@ -627,7 +627,7 @@ case class LocalParams(nodeId: PublicKey,
|
||||
fundingKeyPath: DeterministicWallet.KeyPath,
|
||||
dustLimit: Satoshi,
|
||||
maxHtlcValueInFlightMsat: MilliSatoshi,
|
||||
requestedChannelReserve_opt: Option[Satoshi],
|
||||
initialRequestedChannelReserve_opt: Option[Satoshi],
|
||||
htlcMinimum: MilliSatoshi,
|
||||
toSelfDelay: CltvExpiryDelta,
|
||||
maxAcceptedHtlcs: Int,
|
||||
@ -642,7 +642,7 @@ case class LocalParams(nodeId: PublicKey,
|
||||
case class RemoteParams(nodeId: PublicKey,
|
||||
dustLimit: Satoshi,
|
||||
maxHtlcValueInFlightMsat: UInt64, // this is not MilliSatoshi because it can exceed the total amount of MilliSatoshi
|
||||
requestedChannelReserve_opt: Option[Satoshi],
|
||||
initialRequestedChannelReserve_opt: Option[Satoshi],
|
||||
htlcMinimum: MilliSatoshi,
|
||||
toSelfDelay: CltvExpiryDelta,
|
||||
maxAcceptedHtlcs: Int,
|
||||
|
@ -27,8 +27,8 @@ case class ChannelParams(channelId: ByteVector32,
|
||||
channelFlags: ChannelFlags) {
|
||||
|
||||
require(channelFeatures.paysDirectlyToWallet == localParams.walletStaticPaymentBasepoint.isDefined, s"localParams.walletStaticPaymentBasepoint must be defined only for commitments that pay directly to our wallet (channel features: $channelFeatures")
|
||||
require(channelFeatures.hasFeature(Features.DualFunding) == localParams.requestedChannelReserve_opt.isEmpty, "custom local channel reserve is incompatible with dual-funded channels")
|
||||
require(channelFeatures.hasFeature(Features.DualFunding) == remoteParams.requestedChannelReserve_opt.isEmpty, "custom remote channel reserve is incompatible with dual-funded channels")
|
||||
require(channelFeatures.hasFeature(Features.DualFunding) == localParams.initialRequestedChannelReserve_opt.isEmpty, "custom local channel reserve is incompatible with dual-funded channels")
|
||||
require(channelFeatures.hasFeature(Features.DualFunding) == remoteParams.initialRequestedChannelReserve_opt.isEmpty, "custom remote channel reserve is incompatible with dual-funded channels")
|
||||
|
||||
val commitmentFormat: CommitmentFormat = channelFeatures.commitmentFormat
|
||||
val channelType: SupportedChannelType = channelFeatures.channelType
|
||||
@ -103,17 +103,17 @@ case class ChannelParams(channelId: ByteVector32,
|
||||
def minDepthDualFunding(defaultMinDepth: Int, sharedTx: SharedTransaction): Option[Long] = minDepthDualFunding(defaultMinDepth, sharedTx.sharedOutput.amount, Some(sharedTx.remoteInputs.nonEmpty))
|
||||
|
||||
/** Channel reserve that applies to our funds. */
|
||||
def localChannelReserveForCapacity(capacity: Satoshi): Satoshi = if (channelFeatures.hasFeature(Features.DualFunding)) {
|
||||
def localChannelReserveForCapacity(capacity: Satoshi, isSplice: Boolean): Satoshi = if (channelFeatures.hasFeature(Features.DualFunding) || isSplice) {
|
||||
(capacity / 100).max(remoteParams.dustLimit)
|
||||
} else {
|
||||
remoteParams.requestedChannelReserve_opt.get // this is guarded by a require() in Params
|
||||
remoteParams.initialRequestedChannelReserve_opt.get // this is guarded by a require() in Params
|
||||
}
|
||||
|
||||
/** Channel reserve that applies to our peer's funds. */
|
||||
def remoteChannelReserveForCapacity(capacity: Satoshi): Satoshi = if (channelFeatures.hasFeature(Features.DualFunding)) {
|
||||
def remoteChannelReserveForCapacity(capacity: Satoshi, isSplice: Boolean): Satoshi = if (channelFeatures.hasFeature(Features.DualFunding) || isSplice) {
|
||||
(capacity / 100).max(localParams.dustLimit)
|
||||
} else {
|
||||
localParams.requestedChannelReserve_opt.get // this is guarded by a require() in Params
|
||||
localParams.initialRequestedChannelReserve_opt.get // this is guarded by a require() in Params
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,10 +268,10 @@ case class Commitment(fundingTxIndex: Long,
|
||||
val capacity: Satoshi = commitInput.txOut.amount
|
||||
|
||||
/** Channel reserve that applies to our funds. */
|
||||
def localChannelReserve(params: ChannelParams): Satoshi = params.localChannelReserveForCapacity(capacity)
|
||||
def localChannelReserve(params: ChannelParams): Satoshi = params.localChannelReserveForCapacity(capacity, fundingTxIndex > 0)
|
||||
|
||||
/** Channel reserve that applies to our peer's funds. */
|
||||
def remoteChannelReserve(params: ChannelParams): Satoshi = params.remoteChannelReserveForCapacity(capacity)
|
||||
def remoteChannelReserve(params: ChannelParams): Satoshi = params.remoteChannelReserveForCapacity(capacity, fundingTxIndex > 0)
|
||||
|
||||
// NB: when computing availableBalanceForSend and availableBalanceForReceive, the initiator keeps an extra buffer on
|
||||
// top of its usual channel reserve to avoid getting channels stuck in case the on-chain feerate increases (see
|
||||
|
@ -148,7 +148,7 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
|
||||
nodeId = remoteNodeId,
|
||||
dustLimit = open.dustLimit,
|
||||
maxHtlcValueInFlightMsat = open.maxHtlcValueInFlightMsat,
|
||||
requestedChannelReserve_opt = None, // channel reserve will be computed based on channel capacity
|
||||
initialRequestedChannelReserve_opt = None, // channel reserve will be computed based on channel capacity
|
||||
htlcMinimum = open.htlcMinimum,
|
||||
toSelfDelay = open.toSelfDelay,
|
||||
maxAcceptedHtlcs = open.maxAcceptedHtlcs,
|
||||
@ -247,7 +247,7 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
|
||||
nodeId = remoteNodeId,
|
||||
dustLimit = accept.dustLimit,
|
||||
maxHtlcValueInFlightMsat = accept.maxHtlcValueInFlightMsat,
|
||||
requestedChannelReserve_opt = None, // channel reserve will be computed based on channel capacity
|
||||
initialRequestedChannelReserve_opt = None, // channel reserve will be computed based on channel capacity
|
||||
htlcMinimum = accept.htlcMinimum,
|
||||
toSelfDelay = accept.toSelfDelay,
|
||||
maxAcceptedHtlcs = accept.maxAcceptedHtlcs,
|
||||
|
@ -85,7 +85,7 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
|
||||
pushMsat = input.pushAmount_opt.getOrElse(0 msat),
|
||||
dustLimitSatoshis = input.localParams.dustLimit,
|
||||
maxHtlcValueInFlightMsat = UInt64(input.localParams.maxHtlcValueInFlightMsat.toLong),
|
||||
channelReserveSatoshis = input.localParams.requestedChannelReserve_opt.get,
|
||||
channelReserveSatoshis = input.localParams.initialRequestedChannelReserve_opt.get,
|
||||
htlcMinimumMsat = input.localParams.htlcMinimum,
|
||||
feeratePerKw = input.commitTxFeerate,
|
||||
toSelfDelay = input.localParams.toSelfDelay,
|
||||
@ -114,7 +114,7 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
|
||||
nodeId = remoteNodeId,
|
||||
dustLimit = open.dustLimitSatoshis,
|
||||
maxHtlcValueInFlightMsat = open.maxHtlcValueInFlightMsat,
|
||||
requestedChannelReserve_opt = Some(open.channelReserveSatoshis), // our peer requires us to always have at least that much satoshis in our balance
|
||||
initialRequestedChannelReserve_opt = Some(open.channelReserveSatoshis), // our peer requires us to always have at least that much satoshis in our balance
|
||||
htlcMinimum = open.htlcMinimumMsat,
|
||||
toSelfDelay = open.toSelfDelay,
|
||||
maxAcceptedHtlcs = open.maxAcceptedHtlcs,
|
||||
@ -136,7 +136,7 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
|
||||
val accept = AcceptChannel(temporaryChannelId = open.temporaryChannelId,
|
||||
dustLimitSatoshis = d.initFundee.localParams.dustLimit,
|
||||
maxHtlcValueInFlightMsat = UInt64(d.initFundee.localParams.maxHtlcValueInFlightMsat.toLong),
|
||||
channelReserveSatoshis = d.initFundee.localParams.requestedChannelReserve_opt.get,
|
||||
channelReserveSatoshis = d.initFundee.localParams.initialRequestedChannelReserve_opt.get,
|
||||
minimumDepth = minimumDepth.getOrElse(0),
|
||||
htlcMinimumMsat = d.initFundee.localParams.htlcMinimum,
|
||||
toSelfDelay = d.initFundee.localParams.toSelfDelay,
|
||||
@ -172,7 +172,7 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
|
||||
nodeId = remoteNodeId,
|
||||
dustLimit = accept.dustLimitSatoshis,
|
||||
maxHtlcValueInFlightMsat = accept.maxHtlcValueInFlightMsat,
|
||||
requestedChannelReserve_opt = Some(accept.channelReserveSatoshis), // our peer requires us to always have at least that much satoshis in our balance
|
||||
initialRequestedChannelReserve_opt = Some(accept.channelReserveSatoshis), // our peer requires us to always have at least that much satoshis in our balance
|
||||
htlcMinimum = accept.htlcMinimumMsat,
|
||||
toSelfDelay = accept.toSelfDelay,
|
||||
maxAcceptedHtlcs = accept.maxAcceptedHtlcs,
|
||||
|
@ -649,7 +649,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
|
||||
}
|
||||
|
||||
val sharedInput_opt = fundingParams.sharedInput_opt.map(_ => {
|
||||
val remoteReserve = channelParams.remoteChannelReserveForCapacity(fundingParams.fundingAmount)
|
||||
val remoteReserve = channelParams.remoteChannelReserveForCapacity(fundingParams.fundingAmount, isSplice = true)
|
||||
// We ignore the reserve requirement if we are splicing funds into the channel, which increases the size of the reserve.
|
||||
if (sharedOutput.remoteAmount < remoteReserve && remoteOutputs.nonEmpty && localInputs.isEmpty) {
|
||||
log.warn("invalid interactive tx: peer takes too much funds out and falls below the channel reserve ({} < {})", sharedOutput.remoteAmount, remoteReserve)
|
||||
|
@ -97,7 +97,7 @@ object OpenChannelInterceptor {
|
||||
nodeParams.channelKeyManager.newFundingKeyPath(isInitiator), // we make sure that initiator and non-initiator key paths end differently
|
||||
dustLimit = nodeParams.channelConf.dustLimit,
|
||||
maxHtlcValueInFlightMsat = maxHtlcValueInFlightMsat,
|
||||
requestedChannelReserve_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
|
||||
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
|
||||
htlcMinimum = nodeParams.channelConf.htlcMinimum,
|
||||
toSelfDelay = nodeParams.channelConf.toRemoteDelay, // we choose their delay
|
||||
maxAcceptedHtlcs = nodeParams.channelConf.maxAcceptedHtlcs,
|
||||
|
@ -199,7 +199,7 @@ private[channel] object ChannelTypes0 {
|
||||
nodeId = nodeId,
|
||||
dustLimit = dustLimit,
|
||||
maxHtlcValueInFlightMsat = maxHtlcValueInFlightMsat,
|
||||
requestedChannelReserve_opt = requestedChannelReserve_opt,
|
||||
initialRequestedChannelReserve_opt = requestedChannelReserve_opt,
|
||||
htlcMinimum = htlcMinimum,
|
||||
toSelfDelay = toSelfDelay,
|
||||
maxAcceptedHtlcs = maxAcceptedHtlcs,
|
||||
|
@ -3,7 +3,6 @@ package fr.acinq.eclair.wire.internal.channel.version4
|
||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
|
||||
import fr.acinq.bitcoin.scalacompat.{OutPoint, ScriptWitness, Transaction, TxOut}
|
||||
import fr.acinq.eclair.blockchain.fee
|
||||
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget}
|
||||
import fr.acinq.eclair.channel.LocalFundingStatus._
|
||||
import fr.acinq.eclair.channel._
|
||||
|
@ -10,7 +10,7 @@
|
||||
"fundingKeyPath" : [ 1457788542, 1007597768, 1455922339, 479707306 ],
|
||||
"dustLimit" : 546,
|
||||
"maxHtlcValueInFlightMsat" : 5000000000,
|
||||
"requestedChannelReserve_opt" : 167772,
|
||||
"initialRequestedChannelReserve_opt" : 167772,
|
||||
"htlcMinimum" : 1,
|
||||
"toSelfDelay" : 720,
|
||||
"maxAcceptedHtlcs" : 30,
|
||||
@ -29,7 +29,7 @@
|
||||
"nodeId" : "034fe52e98a0e9d3c21b767e1b371881265d8c7578c21f5afd6d6438da10348b36",
|
||||
"dustLimit" : 573,
|
||||
"maxHtlcValueInFlightMsat" : 16609443000,
|
||||
"requestedChannelReserve_opt" : 167772,
|
||||
"initialRequestedChannelReserve_opt" : 167772,
|
||||
"htlcMinimum" : 1000,
|
||||
"toSelfDelay" : 2016,
|
||||
"maxAcceptedHtlcs" : 483,
|
||||
|
@ -10,7 +10,7 @@
|
||||
"fundingKeyPath" : [ 3561221353, 3653515793, 2711311691, 2863050005 ],
|
||||
"dustLimit" : 546,
|
||||
"maxHtlcValueInFlightMsat" : 1000000000,
|
||||
"requestedChannelReserve_opt" : 150000,
|
||||
"initialRequestedChannelReserve_opt" : 150000,
|
||||
"htlcMinimum" : 1,
|
||||
"toSelfDelay" : 144,
|
||||
"maxAcceptedHtlcs" : 30,
|
||||
@ -29,7 +29,7 @@
|
||||
"nodeId" : "0269a94e8b32c005e4336bfb743c08a6e9beb13d940d57c479d95c8e687ccbdb9f",
|
||||
"dustLimit" : 573,
|
||||
"maxHtlcValueInFlightMsat" : 14850000000,
|
||||
"requestedChannelReserve_opt" : 150000,
|
||||
"initialRequestedChannelReserve_opt" : 150000,
|
||||
"htlcMinimum" : 1000,
|
||||
"toSelfDelay" : 1802,
|
||||
"maxAcceptedHtlcs" : 483,
|
||||
|
@ -10,7 +10,7 @@
|
||||
"fundingKeyPath" : [ 2353764507, 3184449568, 2809819526, 3258060413, 392846475, 1545000620, 720603293, 1808318336, 2147483649 ],
|
||||
"dustLimit" : 546,
|
||||
"maxHtlcValueInFlightMsat" : 20000000000,
|
||||
"requestedChannelReserve_opt" : 150000,
|
||||
"initialRequestedChannelReserve_opt" : 150000,
|
||||
"htlcMinimum" : 1,
|
||||
"toSelfDelay" : 720,
|
||||
"maxAcceptedHtlcs" : 30,
|
||||
@ -36,7 +36,7 @@
|
||||
"nodeId" : "027455aef8453d92f4706b560b61527cc217ddf14da41770e8ed6607190a1851b8",
|
||||
"dustLimit" : 573,
|
||||
"maxHtlcValueInFlightMsat" : 14850000000,
|
||||
"requestedChannelReserve_opt" : 150000,
|
||||
"initialRequestedChannelReserve_opt" : 150000,
|
||||
"htlcMinimum" : 1,
|
||||
"toSelfDelay" : 1802,
|
||||
"maxAcceptedHtlcs" : 483,
|
||||
|
@ -10,7 +10,7 @@
|
||||
"fundingKeyPath" : [ 4092535092, 4227137620, 3959690417, 2298849496, 2106263857, 1090614243, 1495530077, 1280982866, 2147483649 ],
|
||||
"dustLimit" : 1100,
|
||||
"maxHtlcValueInFlightMsat" : 500000000,
|
||||
"requestedChannelReserve_opt" : 10000,
|
||||
"initialRequestedChannelReserve_opt" : 10000,
|
||||
"htlcMinimum" : 0,
|
||||
"toSelfDelay" : 144,
|
||||
"maxAcceptedHtlcs" : 100,
|
||||
@ -32,7 +32,7 @@
|
||||
"nodeId" : "02bbbb671d15145722fb8c28d732cddb249bcc6652ed2b297ff1f77a18371b1e63",
|
||||
"dustLimit" : 1000,
|
||||
"maxHtlcValueInFlightMsat" : 18446744073709551615,
|
||||
"requestedChannelReserve_opt" : 20000,
|
||||
"initialRequestedChannelReserve_opt" : 20000,
|
||||
"htlcMinimum" : 1000,
|
||||
"toSelfDelay" : 144,
|
||||
"maxAcceptedHtlcs" : 30,
|
||||
|
@ -10,7 +10,7 @@
|
||||
"fundingKeyPath" : [ 303987973, 3198768511, 3783619274, 2277156978, 1699864653, 63358126, 3265052696, 516813756, 2147483649 ],
|
||||
"dustLimit" : 1100,
|
||||
"maxHtlcValueInFlightMsat" : 500000000,
|
||||
"requestedChannelReserve_opt" : 10000,
|
||||
"initialRequestedChannelReserve_opt" : 10000,
|
||||
"htlcMinimum" : 0,
|
||||
"toSelfDelay" : 144,
|
||||
"maxAcceptedHtlcs" : 100,
|
||||
@ -33,7 +33,7 @@
|
||||
"nodeId" : "02bbbb671d15145722fb8c28d732cddb249bcc6652ed2b297ff1f77a18371b1e63",
|
||||
"dustLimit" : 1000,
|
||||
"maxHtlcValueInFlightMsat" : 1000000000,
|
||||
"requestedChannelReserve_opt" : 20000,
|
||||
"initialRequestedChannelReserve_opt" : 20000,
|
||||
"htlcMinimum" : 1000,
|
||||
"toSelfDelay" : 144,
|
||||
"maxAcceptedHtlcs" : 30,
|
||||
|
@ -234,7 +234,7 @@ object TestConstants {
|
||||
fundingSatoshis,
|
||||
unlimitedMaxHtlcValueInFlight = false,
|
||||
).copy(
|
||||
requestedChannelReserve_opt = Some(10_000 sat) // Bob will need to keep that much satoshis in his balance
|
||||
initialRequestedChannelReserve_opt = Some(10_000 sat) // Bob will need to keep that much satoshis in his balance
|
||||
)
|
||||
}
|
||||
|
||||
@ -396,7 +396,7 @@ object TestConstants {
|
||||
fundingSatoshis,
|
||||
unlimitedMaxHtlcValueInFlight = false,
|
||||
).copy(
|
||||
requestedChannelReserve_opt = Some(20_000 sat) // Alice will need to keep that much satoshis in her balance
|
||||
initialRequestedChannelReserve_opt = Some(20_000 sat) // Alice will need to keep that much satoshis in her balance
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
|
||||
.modify(_.onChainFeeConf.spendAnchorWithoutHtlcs).setToIf(tags.contains(ChannelStateTestsTags.DontSpendAnchorWithoutHtlcs))(false)
|
||||
val wallet = wallet_opt match {
|
||||
case Some(wallet) => wallet
|
||||
case None => if (tags.contains(ChannelStateTestsTags.DualFunding)) new SingleKeyOnChainWallet() else new DummyOnChainWallet()
|
||||
case None => if (tags.contains(ChannelStateTestsTags.DualFunding) || tags.contains(ChannelStateTestsTags.Splicing)) new SingleKeyOnChainWallet() else new DummyOnChainWallet()
|
||||
}
|
||||
val alice: TestFSMRef[ChannelState, ChannelData, Channel] = {
|
||||
implicit val system: ActorSystem = systemA
|
||||
@ -213,7 +213,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
|
||||
.modify(_.maxHtlcValueInFlightMsat).setToIf(tags.contains(ChannelStateTestsTags.AliceLowMaxHtlcValueInFlight))(150_000_000 msat)
|
||||
.modify(_.dustLimit).setToIf(tags.contains(ChannelStateTestsTags.HighDustLimitDifferenceAliceBob))(5000 sat)
|
||||
.modify(_.dustLimit).setToIf(tags.contains(ChannelStateTestsTags.HighDustLimitDifferenceBobAlice))(1000 sat)
|
||||
.modify(_.requestedChannelReserve_opt).setToIf(tags.contains(ChannelStateTestsTags.DualFunding))(None)
|
||||
.modify(_.initialRequestedChannelReserve_opt).setToIf(tags.contains(ChannelStateTestsTags.DualFunding))(None)
|
||||
.modify(_.upfrontShutdownScript_opt).setToIf(tags.contains(ChannelStateTestsTags.UpfrontShutdownScript))(Some(Script.write(Script.pay2wpkh(Await.result(wallet.getP2wpkhPubkey(), 10 seconds)))))
|
||||
val bobParams = Bob.channelParams
|
||||
.modify(_.initFeatures).setTo(bobInitFeatures)
|
||||
@ -221,7 +221,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually {
|
||||
.modify(_.maxHtlcValueInFlightMsat).setToIf(tags.contains(ChannelStateTestsTags.NoMaxHtlcValueInFlight))(Long.MaxValue.msat)
|
||||
.modify(_.dustLimit).setToIf(tags.contains(ChannelStateTestsTags.HighDustLimitDifferenceAliceBob))(1000 sat)
|
||||
.modify(_.dustLimit).setToIf(tags.contains(ChannelStateTestsTags.HighDustLimitDifferenceBobAlice))(5000 sat)
|
||||
.modify(_.requestedChannelReserve_opt).setToIf(tags.contains(ChannelStateTestsTags.DualFunding))(None)
|
||||
.modify(_.initialRequestedChannelReserve_opt).setToIf(tags.contains(ChannelStateTestsTags.DualFunding))(None)
|
||||
.modify(_.upfrontShutdownScript_opt).setToIf(tags.contains(ChannelStateTestsTags.UpfrontShutdownScript))(Some(Script.write(Script.pay2wpkh(Await.result(wallet.getP2wpkhPubkey(), 10 seconds)))))
|
||||
|
||||
(aliceParams, bobParams, channelType)
|
||||
|
@ -159,6 +159,35 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.spec.toRemote == 700_000_000.msat)
|
||||
}
|
||||
|
||||
test("recv CMD_SPLICE (splice-in, non dual-funded channel)") { () =>
|
||||
val f = init(tags = Set(ChannelStateTestsTags.DualFunding, ChannelStateTestsTags.Splicing))
|
||||
import f._
|
||||
|
||||
reachNormal(f, tags = Set(ChannelStateTestsTags.Splicing)) // we open a non dual-funded channel
|
||||
alice2bob.ignoreMsg { case _: ChannelUpdate => true }
|
||||
bob2alice.ignoreMsg { case _: ChannelUpdate => true }
|
||||
awaitCond(alice.stateName == NORMAL && bob.stateName == NORMAL)
|
||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
assert(!initialState.commitments.params.channelFeatures.hasFeature(Features.DualFunding))
|
||||
assert(initialState.commitments.latest.capacity == 1_000_000.sat)
|
||||
assert(initialState.commitments.latest.localCommit.spec.toLocal == 800_000_000.msat)
|
||||
assert(initialState.commitments.latest.localCommit.spec.toRemote == 200_000_000.msat)
|
||||
// The channel reserve is set by each participant when not using dual-funding.
|
||||
assert(initialState.commitments.latest.localChannelReserve == 20_000.sat)
|
||||
assert(initialState.commitments.latest.remoteChannelReserve == 10_000.sat)
|
||||
|
||||
// We can splice on top of a non dual-funded channel.
|
||||
initiateSplice(f, spliceIn_opt = Some(SpliceIn(500_000 sat)))
|
||||
val postSpliceState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||
assert(!postSpliceState.commitments.params.channelFeatures.hasFeature(Features.DualFunding))
|
||||
assert(postSpliceState.commitments.latest.capacity == 1_500_000.sat)
|
||||
assert(postSpliceState.commitments.latest.localCommit.spec.toLocal == 1_300_000_000.msat)
|
||||
assert(postSpliceState.commitments.latest.localCommit.spec.toRemote == 200_000_000.msat)
|
||||
// The channel reserve is now implicitly set to 1% of the channel capacity on both sides.
|
||||
assert(postSpliceState.commitments.latest.localChannelReserve == 15_000.sat)
|
||||
assert(postSpliceState.commitments.latest.remoteChannelReserve == 15_000.sat)
|
||||
}
|
||||
|
||||
test("recv CMD_SPLICE (splice-out)") { f =>
|
||||
import f._
|
||||
|
||||
|
@ -162,7 +162,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||
| "fundingKeyPath": [42],
|
||||
| "dustLimit": 546,
|
||||
| "maxHtlcValueInFlightMsat": 9223372036854775807,
|
||||
| "requestedChannelReserve_opt": 1000,
|
||||
| "initialRequestedChannelReserve_opt": 1000,
|
||||
| "htlcMinimum": 1,
|
||||
| "toSelfDelay": 144,
|
||||
| "maxAcceptedHtlcs": 50,
|
||||
@ -173,7 +173,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||
| "nodeId": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
| "dustLimit": 546,
|
||||
| "maxHtlcValueInFlightMsat": 18446744073709551615,
|
||||
| "requestedChannelReserve_opt": 1000,
|
||||
| "initialRequestedChannelReserve_opt": 1000,
|
||||
| "htlcMinimum": 1,
|
||||
| "toSelfDelay": 144,
|
||||
| "maxAcceptedHtlcs": 50,
|
||||
|
@ -257,7 +257,7 @@ object ChannelCodecsSpec {
|
||||
fundingKeyPath = DeterministicWallet.KeyPath(Seq(42L)),
|
||||
dustLimit = Satoshi(546),
|
||||
maxHtlcValueInFlightMsat = 50_000_000 msat,
|
||||
requestedChannelReserve_opt = Some(10000 sat),
|
||||
initialRequestedChannelReserve_opt = Some(10000 sat),
|
||||
htlcMinimum = 10000 msat,
|
||||
toSelfDelay = CltvExpiryDelta(144),
|
||||
maxAcceptedHtlcs = 50,
|
||||
@ -270,7 +270,7 @@ object ChannelCodecsSpec {
|
||||
nodeId = randomKey().publicKey,
|
||||
dustLimit = 546 sat,
|
||||
maxHtlcValueInFlightMsat = UInt64(5000000),
|
||||
requestedChannelReserve_opt = Some(10000 sat),
|
||||
initialRequestedChannelReserve_opt = Some(10000 sat),
|
||||
htlcMinimum = 5000 msat,
|
||||
toSelfDelay = CltvExpiryDelta(144),
|
||||
maxAcceptedHtlcs = 50,
|
||||
|
@ -86,8 +86,8 @@ class ChannelCodecs4Spec extends AnyFunSuite {
|
||||
val remoteCodec = remoteParamsCodec(ChannelFeatures(Features.DualFunding))
|
||||
val decodedLocalParams = localCodec.decode(localCodec.encode(localParams).require).require.value
|
||||
val decodedRemoteParams = remoteCodec.decode(remoteCodec.encode(remoteParams).require).require.value
|
||||
assert(decodedLocalParams == localParams.copy(requestedChannelReserve_opt = None))
|
||||
assert(decodedRemoteParams == remoteParams.copy(requestedChannelReserve_opt = None))
|
||||
assert(decodedLocalParams == localParams.copy(initialRequestedChannelReserve_opt = None))
|
||||
assert(decodedRemoteParams == remoteParams.copy(initialRequestedChannelReserve_opt = None))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user