1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 02:27:32 +01:00

Harden requirements on htlc-minimum-msat (#1339)

We were allowing users to set htlc-minimum-msat to 0, which directly
contradicts the fact that we must never send an HTLC for 0 msat.
We now explicitly disallow that behavior: the minimum is 1 msat.

In case the remote side of a channel had set its htlc-minimum-msat to 0,
we would forward HTLC with a value of 0 msat if a sender crafted such a
payment. The spec disallows that, so we now explicitly check for that
lower bound.
This commit is contained in:
Bastien Teinturier 2020-03-16 16:51:29 +01:00 committed by GitHub
parent ab7b373d58
commit 2df07277bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 5 deletions

View File

@ -161,6 +161,9 @@ object NodeParams {
require(dustLimitSatoshis >= Channel.MIN_DUSTLIMIT, s"dust limit must be greater than ${Channel.MIN_DUSTLIMIT}")
}
val htlcMinimum = MilliSatoshi(config.getInt("htlc-minimum-msat"))
require(htlcMinimum > 0.msat, "htlc-minimum-msat must be strictly greater than 0")
val maxAcceptedHtlcs = config.getInt("max-accepted-htlcs")
require(maxAcceptedHtlcs <= Channel.MAX_ACCEPTED_HTLCS, s"max-accepted-htlcs must be lower than ${Channel.MAX_ACCEPTED_HTLCS}")
@ -242,7 +245,7 @@ object NodeParams {
maxAcceptedHtlcs = maxAcceptedHtlcs,
expiryDeltaBlocks = expiryDeltaBlocks,
fulfillSafetyBeforeTimeoutBlocks = fulfillSafetyBeforeTimeoutBlocks,
htlcMinimum = MilliSatoshi(config.getInt("htlc-minimum-msat")),
htlcMinimum = htlcMinimum,
toRemoteDelayBlocks = CltvExpiryDelta(config.getInt("to-remote-delay-blocks")),
maxToLocalDelayBlocks = CltvExpiryDelta(config.getInt("max-to-local-delay-blocks")),
minDepthBlocks = config.getInt("mindepth-blocks"),

View File

@ -175,8 +175,8 @@ object Commitments {
return Failure(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockCount = blockHeight))
}
if (cmd.amount < commitments.remoteParams.htlcMinimum) {
return Failure(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimum, actual = cmd.amount))
if (cmd.amount < commitments.remoteParams.htlcMinimum.max(1 msat)) {
return Failure(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimum.max(1 msat), actual = cmd.amount))
}
// let's compute the current commitment *as seen by them* with this change taken into account
@ -225,8 +225,8 @@ object Commitments {
throw UnexpectedHtlcId(commitments.channelId, expected = commitments.remoteNextHtlcId, actual = add.id)
}
if (add.amountMsat < commitments.localParams.htlcMinimum) {
throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimum, actual = add.amountMsat)
if (add.amountMsat < commitments.localParams.htlcMinimum.max(1 msat)) {
throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimum.max(1 msat), actual = add.amountMsat)
}
// let's compute the current commitment *as seen by us* including this change

View File

@ -82,4 +82,9 @@ class StartupSpec extends FunSuite {
assert(Try(makeNodeParamsWithDefaults(illegalFeaturesConf.withFallback(defaultConf))).isFailure)
}
test("NodeParams should fail if htlc-minimum-msat is set to 0") {
val noHtlcMinimumConf = ConfigFactory.parseString("htlc-minimum-msat = 0")
assert(Try(makeNodeParamsWithDefaults(noHtlcMinimumConf.withFallback(defaultConf))).isFailure)
}
}

View File

@ -174,6 +174,19 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods {
alice2bob.expectNoMsg(200 millis)
}
test("recv CMD_ADD_HTLC (0 msat)") { f =>
import f._
val sender = TestProbe()
// Alice has a minimum set to 0 msat (which should be invalid, but may mislead Bob into relaying 0-value HTLCs which is forbidden by the spec).
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localParams.htlcMinimum === 0.msat)
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
val add = CMD_ADD_HTLC(0 msat, randomBytes32, CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, Upstream.Local(UUID.randomUUID()))
sender.send(bob, add)
val error = HtlcValueTooSmall(channelId(bob), 1 msat, 0 msat)
sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Origin.Local(add.upstream.asInstanceOf[Upstream.Local].id, Some(sender.ref)), Some(initialState.channelUpdate), Some(add))))
bob2alice.expectNoMsg(200 millis)
}
test("recv CMD_ADD_HTLC (increasing balance but still below reserve)", Tag("no_push_msat")) { f =>
import f._
val sender = TestProbe()