1
0
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:
Bastien Teinturier 2023-09-26 11:22:00 +02:00 committed by GitHub
parent 70d150bff6
commit d274fc1939
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 69 additions and 41 deletions

View File

@ -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,

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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
)
}

View File

@ -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)

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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))
}
}