Skip to content

Commit

Permalink
Reject unreasonable remote dust limit (#1694)
Browse files Browse the repository at this point in the history
It makes no sense to use a dust limit that's much higher than what bitcoin
mandates, so we should not allow our peer to use invalid values.
  • Loading branch information
t-bast committed Feb 17, 2021
1 parent 82e5b59 commit 3a94a80
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 2 deletions.
1 change: 1 addition & 0 deletions eclair-core/src/main/resources/reference.conf
Expand Up @@ -64,6 +64,7 @@ eclair {
channel-flags = 1 // announce channels

dust-limit-satoshis = 546
max-remote-dust-limit-satoshis = 600
max-htlc-value-in-flight-msat = 5000000000 // 50 mBTC
htlc-minimum-msat = 1
max-accepted-htlcs = 30
Expand Down
2 changes: 2 additions & 0 deletions eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala
Expand Up @@ -58,6 +58,7 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
syncWhitelist: Set[PublicKey],
pluginParams: Seq[PluginParams],
dustLimit: Satoshi,
maxRemoteDustLimit: Satoshi,
onChainFeeConf: OnChainFeeConf,
maxHtlcValueInFlightMsat: UInt64,
maxAcceptedHtlcs: Int,
Expand Down Expand Up @@ -323,6 +324,7 @@ object NodeParams extends Logging {
overrideFeatures = overrideFeatures,
syncWhitelist = syncWhitelist,
dustLimit = dustLimitSatoshis,
maxRemoteDustLimit = Satoshi(config.getLong("max-remote-dust-limit-satoshis")),
onChainFeeConf = OnChainFeeConf(
feeTargets = feeTargets,
feeEstimator = feeEstimator,
Expand Down
Expand Up @@ -103,6 +103,8 @@ object Helpers {
// BOLT #2: The receiving node MUST fail the channel if: it considers feerate_per_kw too small for timely processing.
if (isFeeTooSmall(open.feeratePerKw)) return Left(FeerateTooSmall(open.temporaryChannelId, open.feeratePerKw))

if (open.dustLimitSatoshis > nodeParams.maxRemoteDustLimit) return Left(DustLimitTooLarge(open.temporaryChannelId, open.dustLimitSatoshis, nodeParams.maxRemoteDustLimit))

// BOLT #2: The receiving node MUST fail the channel if: dust_limit_satoshis is greater than channel_reserve_satoshis.
if (open.dustLimitSatoshis > open.channelReserveSatoshis) return Left(DustLimitTooLarge(open.temporaryChannelId, open.dustLimitSatoshis, open.channelReserveSatoshis))

Expand Down Expand Up @@ -140,6 +142,8 @@ object Helpers {
if (accept.dustLimitSatoshis < Channel.MIN_DUSTLIMIT) return Left(DustLimitTooSmall(accept.temporaryChannelId, accept.dustLimitSatoshis, Channel.MIN_DUSTLIMIT))
}

if (accept.dustLimitSatoshis > nodeParams.maxRemoteDustLimit) return Left(DustLimitTooLarge(open.temporaryChannelId, accept.dustLimitSatoshis, nodeParams.maxRemoteDustLimit))

// BOLT #2: The receiving node MUST fail the channel if: dust_limit_satoshis is greater than channel_reserve_satoshis.
if (accept.dustLimitSatoshis > accept.channelReserveSatoshis) return Left(DustLimitTooLarge(accept.temporaryChannelId, accept.dustLimitSatoshis, accept.channelReserveSatoshis))

Expand Down
Expand Up @@ -167,6 +167,7 @@ object TestConstants {
overrideFeatures = Map.empty,
syncWhitelist = Set.empty,
dustLimit = 1100 sat,
maxRemoteDustLimit = 1500 sat,
onChainFeeConf = OnChainFeeConf(
feeTargets = FeeTargets(6, 2, 2, 6),
feeEstimator = new TestFeeEstimator,
Expand Down Expand Up @@ -271,6 +272,7 @@ object TestConstants {
overrideFeatures = Map.empty,
syncWhitelist = Set.empty,
dustLimit = 1000 sat,
maxRemoteDustLimit = 1500 sat,
onChainFeeConf = OnChainFeeConf(
feeTargets = FeeTargets(6, 2, 2, 6),
feeEstimator = new TestFeeEstimator,
Expand Down
Expand Up @@ -52,6 +52,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
val aliceNodeParams = Alice.nodeParams
.modify(_.chainHash).setToIf(test.tags.contains("mainnet"))(Block.LivenetGenesisBlock.hash)
.modify(_.maxFundingSatoshis).setToIf(test.tags.contains("high-max-funding-size"))(Btc(100))
.modify(_.maxRemoteDustLimit).setToIf(test.tags.contains("high-remote-dust-limit"))(15000 sat)
val aliceParams = Alice.channelParams
.modify(_.features).setToIf(test.tags.contains("wumbo"))(Features(Set(ActivatedFeature(Wumbo, Optional))))

Expand Down Expand Up @@ -98,7 +99,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
awaitCond(alice.stateName == CLOSED)
}

test("recv AcceptChannel (invalid dust limit)", Tag("mainnet")) { f =>
test("recv AcceptChannel (dust limit too low)", Tag("mainnet")) { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
// we don't want their dust limit to be below 546
Expand All @@ -109,6 +110,16 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
awaitCond(alice.stateName == CLOSED)
}

test("recv AcceptChannel (dust limit too high)") { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
val highDustLimitSatoshis = 2000.sat
alice ! accept.copy(dustLimitSatoshis = highDustLimitSatoshis)
val error = alice2bob.expectMsgType[Error]
assert(error === Error(accept.temporaryChannelId, DustLimitTooLarge(accept.temporaryChannelId, highDustLimitSatoshis, Alice.nodeParams.maxRemoteDustLimit).getMessage))
awaitCond(alice.stateName == CLOSED)
}

test("recv AcceptChannel (to_self_delay too high)") { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
Expand Down Expand Up @@ -151,7 +162,7 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
awaitCond(alice.stateName == CLOSED)
}

test("recv AcceptChannel (dust limit above our reserve)") { f =>
test("recv AcceptChannel (dust limit above our reserve)", Tag("high-remote-dust-limit")) { f =>
import f._
val accept = bob2alice.expectMsgType[AcceptChannel]
val open = alice.stateData.asInstanceOf[DATA_WAIT_FOR_ACCEPT_CHANNEL].lastSent
Expand Down
Expand Up @@ -188,6 +188,16 @@ class WaitForOpenChannelStateSpec extends TestKitBaseClass with FixtureAnyFunSui
awaitCond(bob.stateName == CLOSED)
}

test("recv OpenChannel (dust limit too high)") { f =>
import f._
val open = alice2bob.expectMsgType[OpenChannel]
val dustLimitTooHigh = 2000.sat
bob ! open.copy(dustLimitSatoshis = dustLimitTooHigh)
val error = bob2alice.expectMsgType[Error]
assert(error === Error(open.temporaryChannelId, DustLimitTooLarge(open.temporaryChannelId, dustLimitTooHigh, Bob.nodeParams.maxRemoteDustLimit).getMessage))
awaitCond(bob.stateName == CLOSED)
}

test("recv OpenChannel (toLocal + toRemote below reserve)") { f =>
import f._
val open = alice2bob.expectMsgType[OpenChannel]
Expand Down

0 comments on commit 3a94a80

Please sign in to comment.