Skip to content

Commit

Permalink
Update anchor outputs feerate tolerance (#1980)
Browse files Browse the repository at this point in the history
Allow any feerate when using anchor outputs and we're fundee.
This will prevent unwanted channel closure.

This can be unsafe in a high fee environment if the commit tx is below
the propagation threshold. However, even when we discover it it's too late
anyway, so our only option is to wait for package relay to save the day.

Ensure feerate is always above propagation threshold when we're funder.
We lift the limit configured by the node operator when it is below the
network propagation threshold.
  • Loading branch information
t-bast authored Oct 4, 2021
1 parent c803da6 commit d6b46ae
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 63 deletions.
4 changes: 2 additions & 2 deletions eclair-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ eclair {
}

feerate-tolerance {
ratio-low = 0.5 // will allow remote fee rates as low as half our local feerate
ratio-high = 10.0 // will allow remote fee rates as high as 10 times our local feerate
ratio-low = 0.5 // will allow remote fee rates as low as half our local feerate when not using anchor outputs
ratio-high = 10.0 // will allow remote fee rates as high as 10 times our local feerate when not using anchor outputs
// when using anchor outputs, we only need to use a commitment feerate that allows the tx to propagate: we will use CPFP to speed up confirmation if needed.
// the following value is the maximum feerate we'll use for our commit tx (in sat/byte)
anchor-output-max-commit-feerate = 10
Expand Down
1 change: 1 addition & 0 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class Setup(val datadir: File,
// @formatter:off
override def getFeeratePerKb(target: Int): FeeratePerKB = feeratesPerKB.get().feePerBlock(target)
override def getFeeratePerKw(target: Int): FeeratePerKw = feeratesPerKw.get().feePerBlock(target)
override def getMempoolMinFeeratePerKw(): FeeratePerKw = feeratesPerKw.get().mempoolMinFee
// @formatter:on
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ trait FeeEstimator {
// @formatter:off
def getFeeratePerKb(target: Int): FeeratePerKB
def getFeeratePerKw(target: Int): FeeratePerKw
def getMempoolMinFeeratePerKw(): FeeratePerKw
// @formatter:on
}

Expand All @@ -43,7 +44,8 @@ case class FeerateTolerance(ratioLow: Double, ratioHigh: Double, anchorOutputMax
case ChannelTypes.Standard | ChannelTypes.StaticRemoteKey =>
proposedFeerate < networkFeerate * ratioLow || networkFeerate * ratioHigh < proposedFeerate
case ChannelTypes.AnchorOutputs | ChannelTypes.AnchorOutputsZeroFeeHtlcTx =>
proposedFeerate < networkFeerate * ratioLow || anchorOutputMaxCommitFeerate * ratioHigh < proposedFeerate
// when using anchor outputs, we allow any feerate: fees will be set with CPFP and RBF at broadcast time
false
}
}
}
Expand All @@ -66,13 +68,16 @@ case class OnChainFeeConf(feeTargets: FeeTargets, feeEstimator: FeeEstimator, cl
* @param currentFeerates_opt if provided, will be used to compute the most up-to-date network fee, otherwise we rely on the fee estimator
*/
def getCommitmentFeerate(remoteNodeId: PublicKey, channelType: SupportedChannelType, channelCapacity: Satoshi, currentFeerates_opt: Option[CurrentFeerates]): FeeratePerKw = {
val networkFeerate = currentFeerates_opt match {
case Some(currentFeerates) => currentFeerates.feeratesPerKw.feePerBlock(feeTargets.commitmentBlockTarget)
case None => feeEstimator.getFeeratePerKw(feeTargets.commitmentBlockTarget)
val (networkFeerate, networkMinFee) = currentFeerates_opt match {
case Some(currentFeerates) => (currentFeerates.feeratesPerKw.feePerBlock(feeTargets.commitmentBlockTarget), currentFeerates.feeratesPerKw.mempoolMinFee)
case None => (feeEstimator.getFeeratePerKw(feeTargets.commitmentBlockTarget), feeEstimator.getMempoolMinFeeratePerKw())
}
channelType.commitmentFormat match {
case Transactions.DefaultCommitmentFormat => networkFeerate
case _: Transactions.AnchorOutputsCommitmentFormat => networkFeerate.min(feerateToleranceFor(remoteNodeId).anchorOutputMaxCommitFeerate)
case _: Transactions.AnchorOutputsCommitmentFormat =>
val targetFeerate = networkFeerate.min(feerateToleranceFor(remoteNodeId).anchorOutputMaxCommitFeerate)
// We make sure the feerate is always greater than the propagation threshold.
targetFeerate.max(networkMinFee * 1.25)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ private class TxPublisher(nodeParams: NodeParams, factory: TxPublisher.ChildFact
}

case WrappedCurrentBlockCount(currentBlockCount) =>
log.debug("retry publishing {} transactions at block {}", retryNextBlock.length, currentBlockCount)
log.info("{} transactions are still pending at block {}, retrying {} transactions that previously failed", pending.size, currentBlockCount, retryNextBlock.length)
retryNextBlock.foreach(cmd => timers.startSingleTimer(cmd, (1 + Random.nextLong(nodeParams.maxTxPublishRetryDelay.toMillis)).millis))
run(pending, Seq.empty, channelInfo)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ object TestConstants {
class TestFeeEstimator extends FeeEstimator {
private var currentFeerates = FeeratesPerKw.single(feeratePerKw)

// @formatter:off
override def getFeeratePerKb(target: Int): FeeratePerKB = FeeratePerKB(currentFeerates.feePerBlock(target))

override def getFeeratePerKw(target: Int): FeeratePerKw = currentFeerates.feePerBlock(target)
override def getMempoolMinFeeratePerKw(): FeeratePerKw = currentFeerates.mempoolMinFee
// @formatter:on

def setFeerate(feeratesPerKw: FeeratesPerKw): Unit = {
currentFeerates = feeratesPerKw
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,36 @@ class FeeEstimatorSpec extends AnyFunSuite {
val overrideMaxCommitFeerate = defaultMaxCommitFeerate * 2
val feeConf = OnChainFeeConf(FeeTargets(1, 2, 1, 1), feeEstimator, closeOnOfflineMismatch = true, updateFeeMinDiffRatio = 0.1, FeerateTolerance(0.5, 2.0, defaultMaxCommitFeerate), Map(overrideNodeId -> FeerateTolerance(0.5, 2.0, overrideMaxCommitFeerate)))

feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate / 2))
feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate / 2, mempoolMinFee = FeeratePerKw(250 sat)))
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputs, 100000 sat, None) === defaultMaxCommitFeerate / 2)
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, None) === defaultMaxCommitFeerate / 2)

feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate * 2))
feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate * 2, mempoolMinFee = FeeratePerKw(250 sat)))
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputs, 100000 sat, None) === defaultMaxCommitFeerate)
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, None) === defaultMaxCommitFeerate)
assert(feeConf.getCommitmentFeerate(overrideNodeId, ChannelTypes.AnchorOutputs, 100000 sat, None) === overrideMaxCommitFeerate)
assert(feeConf.getCommitmentFeerate(overrideNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, None) === overrideMaxCommitFeerate)

val currentFeerates1 = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate / 2))
val currentFeerates1 = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate / 2, mempoolMinFee = FeeratePerKw(250 sat)))
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputs, 100000 sat, Some(currentFeerates1)) === defaultMaxCommitFeerate / 2)
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, Some(currentFeerates1)) === defaultMaxCommitFeerate / 2)

val currentFeerates2 = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate * 1.5))
feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate / 2))
val currentFeerates2 = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate * 1.5, mempoolMinFee = FeeratePerKw(250 sat)))
feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(10000 sat)).copy(blocks_2 = defaultMaxCommitFeerate / 2, mempoolMinFee = FeeratePerKw(250 sat)))
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputs, 100000 sat, Some(currentFeerates2)) === defaultMaxCommitFeerate)
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, Some(currentFeerates2)) === defaultMaxCommitFeerate)

val highFeerates = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(25000 sat)).copy(mempoolMinFee = FeeratePerKw(10000 sat)))
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputs, 100000 sat, Some(highFeerates)) === FeeratePerKw(10000 sat) * 1.25)
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, Some(highFeerates)) === FeeratePerKw(10000 sat) * 1.25)
assert(feeConf.getCommitmentFeerate(overrideNodeId, ChannelTypes.AnchorOutputs, 100000 sat, Some(highFeerates)) === FeeratePerKw(10000 sat) * 1.25)
assert(feeConf.getCommitmentFeerate(overrideNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, Some(highFeerates)) === FeeratePerKw(10000 sat) * 1.25)

feeEstimator.setFeerate(FeeratesPerKw.single(FeeratePerKw(25000 sat)).copy(mempoolMinFee = FeeratePerKw(10000 sat)))
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputs, 100000 sat, None) === FeeratePerKw(10000 sat) * 1.25)
assert(feeConf.getCommitmentFeerate(defaultNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, None) === FeeratePerKw(10000 sat) * 1.25)
assert(feeConf.getCommitmentFeerate(overrideNodeId, ChannelTypes.AnchorOutputs, 100000 sat, None) === FeeratePerKw(10000 sat) * 1.25)
assert(feeConf.getCommitmentFeerate(overrideNodeId, ChannelTypes.AnchorOutputsZeroFeeHtlcTx, 100000 sat, None) === FeeratePerKw(10000 sat) * 1.25)
}

test("fee difference too high") {
Expand All @@ -96,21 +108,21 @@ class FeeEstimatorSpec extends AnyFunSuite {
test("fee difference too high (anchor outputs)") {
val tolerance = FeerateTolerance(ratioLow = 0.5, ratioHigh = 4.0, anchorOutputMaxCommitFeerate = FeeratePerKw(2500 sat))
val testCases = Seq(
(FeeratePerKw(500 sat), FeeratePerKw(500 sat), false),
(FeeratePerKw(500 sat), FeeratePerKw(2500 sat), false),
(FeeratePerKw(500 sat), FeeratePerKw(10000 sat), false),
(FeeratePerKw(500 sat), FeeratePerKw(10001 sat), true),
(FeeratePerKw(2500 sat), FeeratePerKw(10000 sat), false),
(FeeratePerKw(2500 sat), FeeratePerKw(10001 sat), true),
(FeeratePerKw(2500 sat), FeeratePerKw(1250 sat), false),
(FeeratePerKw(2500 sat), FeeratePerKw(1249 sat), true),
(FeeratePerKw(2500 sat), FeeratePerKw(1000 sat), true),
(FeeratePerKw(1000 sat), FeeratePerKw(500 sat), false),
(FeeratePerKw(1000 sat), FeeratePerKw(499 sat), true),
(FeeratePerKw(500 sat), FeeratePerKw(500 sat)),
(FeeratePerKw(500 sat), FeeratePerKw(2500 sat)),
(FeeratePerKw(500 sat), FeeratePerKw(10000 sat)),
(FeeratePerKw(500 sat), FeeratePerKw(10001 sat)),
(FeeratePerKw(2500 sat), FeeratePerKw(10000 sat)),
(FeeratePerKw(2500 sat), FeeratePerKw(10001 sat)),
(FeeratePerKw(2500 sat), FeeratePerKw(1250 sat)),
(FeeratePerKw(2500 sat), FeeratePerKw(1249 sat)),
(FeeratePerKw(2500 sat), FeeratePerKw(1000 sat)),
(FeeratePerKw(1000 sat), FeeratePerKw(500 sat)),
(FeeratePerKw(1000 sat), FeeratePerKw(499 sat)),
)
testCases.foreach { case (networkFeerate, proposedFeerate, expected) =>
assert(tolerance.isFeeDiffTooHigh(ChannelTypes.AnchorOutputs, networkFeerate, proposedFeerate) === expected)
assert(tolerance.isFeeDiffTooHigh(ChannelTypes.AnchorOutputsZeroFeeHtlcTx, networkFeerate, proposedFeerate) === expected)
testCases.foreach { case (networkFeerate, proposedFeerate) =>
assert(!tolerance.isFeeDiffTooHigh(ChannelTypes.AnchorOutputs, networkFeerate, proposedFeerate))
assert(!tolerance.isFeeDiffTooHigh(ChannelTypes.AnchorOutputsZeroFeeHtlcTx, networkFeerate, proposedFeerate))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1800,38 +1800,26 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._

val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val commitTx = initialState.commitments.localCommit.commitTxAndRemoteSig.commitTx.tx
assert(initialState.commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
alice2bob.send(bob, UpdateFee(initialState.channelId, TestConstants.anchorOutputsFeeratePerKw * 3))
bob2alice.expectNoMessage(250 millis) // we don't close because the commitment doesn't contain any HTLC

// when we try to add an HTLC, we still disagree on the feerate so we close
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray).contains("local/remote feerates are too different"))
awaitCond(bob.stateName == CLOSING)
// channel should be advertised as down
assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId)
assert(bob2blockchain.expectMsgType[PublishRawTx].tx.txid === commitTx.txid)
val add = UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)
alice2bob.send(bob, add)
val fee = UpdateFee(initialState.channelId, TestConstants.anchorOutputsFeeratePerKw * 3)
alice2bob.send(bob, fee)
awaitCond(bob.stateData == initialState.copy(commitments = initialState.commitments.copy(remoteChanges = initialState.commitments.remoteChanges.copy(proposed = initialState.commitments.remoteChanges.proposed :+ add :+ fee), remoteNextHtlcId = 1)))
bob2alice.expectNoMessage(250 millis) // we don't close because we're using anchor outputs
}

test("recv UpdateFee (remote feerate is too small, anchor outputs)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
import f._

val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val commitTx = initialState.commitments.localCommit.commitTxAndRemoteSig.commitTx.tx
assert(initialState.commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
alice2bob.send(bob, UpdateFee(initialState.channelId, FeeratePerKw(FeeratePerByte(2 sat))))
bob2alice.expectNoMessage(250 millis) // we don't close because the commitment doesn't contain any HTLC

// when we try to add an HTLC, we still disagree on the feerate so we close
alice2bob.send(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket))
val error = bob2alice.expectMsgType[Error]
assert(new String(error.data.toArray).contains("local/remote feerates are too different"))
awaitCond(bob.stateName == CLOSING)
// channel should be advertised as down
assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId)
assert(bob2blockchain.expectMsgType[PublishRawTx].tx.txid === commitTx.txid)
val add = UpdateAddHtlc(ByteVector32.Zeroes, 0, 2500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket)
alice2bob.send(bob, add)
val fee = UpdateFee(initialState.channelId, FeeratePerKw(FeeratePerByte(2 sat)))
alice2bob.send(bob, fee)
awaitCond(bob.stateData == initialState.copy(commitments = initialState.commitments.copy(remoteChanges = initialState.commitments.remoteChanges.copy(proposed = initialState.commitments.remoteChanges.proposed :+ add :+ fee), remoteNextHtlcId = 1)))
bob2alice.expectNoMessage(250 millis) // we don't close because we're using anchor outputs
}

test("recv UpdateFee (remote feerate is too small)") { f =>
Expand Down Expand Up @@ -2400,8 +2388,12 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
assert(initialState.commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2))
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2).copy(mempoolMinFee = FeeratePerKw(250 sat)))
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw / 2))
alice2bob.expectMsgType[CommitSig]
// The configured maximum feerate is bypassed if it's below the propagation threshold.
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(mempoolMinFee = TestConstants.anchorOutputsFeeratePerKw))
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw * 1.25))
}

test("recv CurrentFeerate (when funder, doesn't trigger an UpdateFee)") { f =>
Expand All @@ -2415,7 +2407,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
import f._
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
assert(initialState.commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2))
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(mempoolMinFee = FeeratePerKw(250 sat)))
alice2bob.expectNoMessage(500 millis)
}

Expand Down Expand Up @@ -2455,13 +2447,12 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
crossSign(alice, bob, alice2bob, bob2alice)
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw / 2)

// The network fees spike, so Bob closes the channel.
// The network fees spike, but Bob doesn't close the channel because we're using anchor outputs.
bob.feeEstimator.setFeerate(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2))
val event = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2))
bob ! event
bob2alice.expectMsgType[Error]
bob2blockchain.expectMsgType[PublishTx] // commit tx
awaitCond(bob.stateName == CLOSING)
bob2alice.expectNoMessage(250 millis)
assert(bob.stateName === NORMAL)
}

test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different, without HTLCs)") { f =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
import f._
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
assert(initialState.commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2))
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2).copy(mempoolMinFee = FeeratePerKw(250 sat)))
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw / 2))
}

Expand All @@ -649,7 +649,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
import f._
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
assert(initialState.commitments.localCommit.spec.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2))
alice ! CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(mempoolMinFee = FeeratePerKw(250 sat)))
alice2bob.expectNoMessage(500 millis)
}

Expand Down
Loading

0 comments on commit d6b46ae

Please sign in to comment.