1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-20 10:39:19 +01:00

added check on to_self_delay parameter (#470)

This commit is contained in:
Pierre-Marie Padiou 2018-03-02 11:25:03 +01:00 committed by Fabrice Drouin
parent 24dadff625
commit bb1d0e96cf
8 changed files with 50 additions and 20 deletions

View File

@ -51,7 +51,8 @@ eclair {
reserve-to-funding-ratio = 0.01 // recommended by BOLT #2
max-reserve-to-funding-ratio = 0.05 // channel reserve can't be more than 5% of the funding amount (recommended: 1%)
delay-blocks = 144
to-remote-delay-blocks = 144 // number of blocks that the other node's to-self outputs must be delayed (144 ~ 1 day)
max-to-local-delay-blocks = 1000 // maximum number of blocks that we are ready to accept for our own delayed outputs (1000 ~ 1 week)
mindepth-blocks = 2
expiry-delta-blocks = 144

View File

@ -32,7 +32,8 @@ case class NodeParams(keyManager: KeyManager,
maxAcceptedHtlcs: Int,
expiryDeltaBlocks: Int,
htlcMinimumMsat: Int,
delayBlocks: Int,
toRemoteDelayBlocks: Int,
maxToLocalDelayBlocks: Int,
minDepthBlocks: Int,
smartfeeNBlocks: Int,
feeBaseMsat: Int,
@ -141,7 +142,8 @@ object NodeParams {
maxAcceptedHtlcs = maxAcceptedHtlcs,
expiryDeltaBlocks = config.getInt("expiry-delta-blocks"),
htlcMinimumMsat = config.getInt("htlc-minimum-msat"),
delayBlocks = config.getInt("delay-blocks"),
toRemoteDelayBlocks = config.getInt("to-remote-delay-blocks"),
maxToLocalDelayBlocks = config.getInt("max-to-local-delay-blocks"),
minDepthBlocks = config.getInt("mindepth-blocks"),
smartfeeNBlocks = 3,
feeBaseMsat = config.getInt("fee-base-msat"),

View File

@ -17,6 +17,7 @@ case class InvalidFundingAmount (override val channelId: BinaryDa
case class InvalidPushAmount (override val channelId: BinaryData, pushMsat: Long, max: Long) extends ChannelException(channelId, s"invalid push_msat=$pushMsat (max=$max)")
case class InvalidMaxAcceptedHtlcs (override val channelId: BinaryData, maxAcceptedHtlcs: Int, max: Int) extends ChannelException(channelId, s"invalid max_accepted_htlcs=$maxAcceptedHtlcs (max=$max)")
case class InvalidDustLimit (override val channelId: BinaryData, dustLimitSatoshis: Long, min: Long) extends ChannelException(channelId, s"invalid dust_limit_satoshis=$dustLimitSatoshis (min=$min)")
case class ToSelfDelayTooHigh (override val channelId: BinaryData, toSelfDelay: Int, max: Int) extends ChannelException(channelId, s"unreasonable to_self_delay=$toSelfDelay (max=$max)")
case class ChannelReserveTooHigh (override val channelId: BinaryData, channelReserveSatoshis: Long, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double) extends ChannelException(channelId, s"channelReserveSatoshis too high: reserve=$channelReserveSatoshis fundingRatio=$reserveToFundingRatio maxFundingRatio=$maxReserveToFundingRatio")
case class ChannelFundingError (override val channelId: BinaryData) extends ChannelException(channelId, "channel funding error")
case class NoMoreHtlcsClosingInProgress (override val channelId: BinaryData) extends ChannelException(channelId, "cannot send new htlcs, closing in progress")

View File

@ -41,30 +41,32 @@ object Helpers {
* Called by the fundee
*/
def validateParamsFundee(nodeParams: NodeParams, open: OpenChannel): Unit = {
if (nodeParams.chainHash != open.chainHash) throw new InvalidChainHash(open.temporaryChannelId, local = nodeParams.chainHash, remote = open.chainHash)
if (open.fundingSatoshis < Channel.MIN_FUNDING_SATOSHIS || open.fundingSatoshis >= Channel.MAX_FUNDING_SATOSHIS) throw new InvalidFundingAmount(open.temporaryChannelId, open.fundingSatoshis, Channel.MIN_FUNDING_SATOSHIS, Channel.MAX_FUNDING_SATOSHIS)
if (open.pushMsat > 1000 * open.fundingSatoshis) throw new InvalidPushAmount(open.temporaryChannelId, open.pushMsat, 1000 * open.fundingSatoshis)
if (nodeParams.chainHash != open.chainHash) throw InvalidChainHash(open.temporaryChannelId, local = nodeParams.chainHash, remote = open.chainHash)
if (open.fundingSatoshis < Channel.MIN_FUNDING_SATOSHIS || open.fundingSatoshis >= Channel.MAX_FUNDING_SATOSHIS) throw InvalidFundingAmount(open.temporaryChannelId, open.fundingSatoshis, Channel.MIN_FUNDING_SATOSHIS, Channel.MAX_FUNDING_SATOSHIS)
if (open.pushMsat > 1000 * open.fundingSatoshis) throw InvalidPushAmount(open.temporaryChannelId, open.pushMsat, 1000 * open.fundingSatoshis)
val localFeeratePerKw = Globals.feeratesPerKw.get.block_1
if (isFeeDiffTooHigh(open.feeratePerKw, localFeeratePerKw, nodeParams.maxFeerateMismatch)) throw new FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.feeratePerKw)
if (isFeeDiffTooHigh(open.feeratePerKw, localFeeratePerKw, nodeParams.maxFeerateMismatch)) throw FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.feeratePerKw)
// only enforce dust limit check on mainnet
if (nodeParams.chainHash == Block.LivenetGenesisBlock.hash) {
if (open.dustLimitSatoshis < Channel.MIN_DUSTLIMIT) throw new InvalidDustLimit(open.temporaryChannelId, open.dustLimitSatoshis, Channel.MIN_DUSTLIMIT)
if (open.dustLimitSatoshis < Channel.MIN_DUSTLIMIT) throw InvalidDustLimit(open.temporaryChannelId, open.dustLimitSatoshis, Channel.MIN_DUSTLIMIT)
}
if (open.toSelfDelay > nodeParams.maxToLocalDelayBlocks) throw ToSelfDelayTooHigh(open.temporaryChannelId, open.toSelfDelay, nodeParams.maxToLocalDelayBlocks)
val reserveToFundingRatio = open.channelReserveSatoshis.toDouble / Math.max(open.fundingSatoshis, 1)
if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw new ChannelReserveTooHigh(open.temporaryChannelId, open.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio)
if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw ChannelReserveTooHigh(open.temporaryChannelId, open.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio)
}
/**
* Called by the funder
*/
def validateParamsFunder(nodeParams: NodeParams, open: OpenChannel, accept: AcceptChannel): Unit = {
if (accept.maxAcceptedHtlcs > Channel.MAX_ACCEPTED_HTLCS) throw new InvalidMaxAcceptedHtlcs(accept.temporaryChannelId, accept.maxAcceptedHtlcs, Channel.MAX_ACCEPTED_HTLCS)
if (accept.maxAcceptedHtlcs > Channel.MAX_ACCEPTED_HTLCS) throw InvalidMaxAcceptedHtlcs(accept.temporaryChannelId, accept.maxAcceptedHtlcs, Channel.MAX_ACCEPTED_HTLCS)
// only enforce dust limit check on mainnet
if (nodeParams.chainHash == Block.LivenetGenesisBlock.hash) {
if (accept.dustLimitSatoshis < Channel.MIN_DUSTLIMIT) throw new InvalidDustLimit(accept.temporaryChannelId, accept.dustLimitSatoshis, Channel.MIN_DUSTLIMIT)
if (accept.dustLimitSatoshis < Channel.MIN_DUSTLIMIT) throw InvalidDustLimit(accept.temporaryChannelId, accept.dustLimitSatoshis, Channel.MIN_DUSTLIMIT)
}
if (accept.toSelfDelay > nodeParams.maxToLocalDelayBlocks) throw ToSelfDelayTooHigh(accept.temporaryChannelId, accept.toSelfDelay, nodeParams.maxToLocalDelayBlocks)
val reserveToFundingRatio = accept.channelReserveSatoshis.toDouble / Math.max(open.fundingSatoshis, 1)
if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw new ChannelReserveTooHigh(open.temporaryChannelId, accept.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio)
if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw ChannelReserveTooHigh(open.temporaryChannelId, accept.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio)
}
/**
@ -209,7 +211,7 @@ object Helpers {
val lastCommitFeeSatoshi = commitments.commitInput.txOut.amount.amount - commitments.localCommit.publishableTxs.commitTx.tx.txOut.map(_.amount.amount).sum
if (remoteClosingFee.amount > lastCommitFeeSatoshi) {
log.error(s"remote proposed a commit fee higher than the last commitment fee: remoteClosingFeeSatoshi=${remoteClosingFee.amount} lastCommitFeeSatoshi=$lastCommitFeeSatoshi")
throw new InvalidCloseFee(commitments.channelId, remoteClosingFee.amount)
throw InvalidCloseFee(commitments.channelId, remoteClosingFee.amount)
}
val (closingTx, closingSigned) = makeClosingTx(keyManager, commitments, localScriptPubkey, remoteScriptPubkey, remoteClosingFee)
val signedClosingTx = Transactions.addSigs(closingTx, keyManager.fundingPublicKey(commitments.localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey, closingSigned.signature, remoteClosingSig)

View File

@ -375,7 +375,7 @@ object Peer {
maxHtlcValueInFlightMsat = nodeParams.maxHtlcValueInFlightMsat,
channelReserveSatoshis = (nodeParams.reserveToFundingRatio * fundingSatoshis).toLong,
htlcMinimumMsat = nodeParams.htlcMinimumMsat,
toSelfDelay = nodeParams.delayBlocks,
toSelfDelay = nodeParams.toRemoteDelayBlocks, // we choose their delay
maxAcceptedHtlcs = nodeParams.maxAcceptedHtlcs,
defaultFinalScriptPubKey = defaultFinalScriptPubKey,
isFunder = isFunder,

View File

@ -41,7 +41,8 @@ object TestConstants {
expiryDeltaBlocks = 144,
htlcMinimumMsat = 0,
minDepthBlocks = 3,
delayBlocks = 144,
toRemoteDelayBlocks = 144,
maxToLocalDelayBlocks = 1000,
smartfeeNBlocks = 3,
feeBaseMsat = 546000,
feeProportionalMillionth = 10,
@ -95,7 +96,8 @@ object TestConstants {
expiryDeltaBlocks = 144,
htlcMinimumMsat = 1000,
minDepthBlocks = 3,
delayBlocks = 144,
toRemoteDelayBlocks = 144,
maxToLocalDelayBlocks = 1000,
smartfeeNBlocks = 3,
feeBaseMsat = 546000,
feeProportionalMillionth = 10,

View File

@ -55,7 +55,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp
val invalidMaxAcceptedHtlcs = 484
alice ! accept.copy(maxAcceptedHtlcs = invalidMaxAcceptedHtlcs)
val error = alice2bob.expectMsgType[Error]
assert(error === Error(accept.temporaryChannelId, new InvalidMaxAcceptedHtlcs(accept.temporaryChannelId, invalidMaxAcceptedHtlcs, Channel.MAX_ACCEPTED_HTLCS).getMessage.getBytes("UTF-8")))
assert(error === Error(accept.temporaryChannelId, InvalidMaxAcceptedHtlcs(accept.temporaryChannelId, invalidMaxAcceptedHtlcs, Channel.MAX_ACCEPTED_HTLCS).getMessage.getBytes("UTF-8")))
awaitCond(alice.stateName == CLOSED)
}
}
@ -67,7 +67,18 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp
val lowDustLimitSatoshis = 545
alice ! accept.copy(dustLimitSatoshis = lowDustLimitSatoshis)
val error = alice2bob.expectMsgType[Error]
assert(error === Error(accept.temporaryChannelId, new InvalidDustLimit(accept.temporaryChannelId, lowDustLimitSatoshis, Channel.MIN_DUSTLIMIT).getMessage.getBytes("UTF-8")))
assert(error === Error(accept.temporaryChannelId, InvalidDustLimit(accept.temporaryChannelId, lowDustLimitSatoshis, Channel.MIN_DUSTLIMIT).getMessage.getBytes("UTF-8")))
awaitCond(alice.stateName == CLOSED)
}
}
test("recv AcceptChannel (to_self_delay too high)") { case (alice, alice2bob, bob2alice, _) =>
within(30 seconds) {
val accept = bob2alice.expectMsgType[AcceptChannel]
val delayTooHigh = 10000
alice ! accept.copy(toSelfDelay = delayTooHigh)
val error = alice2bob.expectMsgType[Error]
assert(error === Error(accept.temporaryChannelId, ToSelfDelayTooHigh(accept.temporaryChannelId, delayTooHigh, Alice.nodeParams.maxToLocalDelayBlocks).getMessage.getBytes("UTF-8")))
awaitCond(alice.stateName == CLOSED)
}
}
@ -79,7 +90,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp
val reserveTooHigh = (0.3 * TestConstants.fundingSatoshis).toLong
alice ! accept.copy(channelReserveSatoshis = reserveTooHigh)
val error = alice2bob.expectMsgType[Error]
assert(error === Error(accept.temporaryChannelId, new ChannelReserveTooHigh(accept.temporaryChannelId, reserveTooHigh, 0.3, 0.05).getMessage.getBytes("UTF-8")))
assert(error === Error(accept.temporaryChannelId, ChannelReserveTooHigh(accept.temporaryChannelId, reserveTooHigh, 0.3, 0.05).getMessage.getBytes("UTF-8")))
awaitCond(alice.stateName == CLOSED)
}
}

View File

@ -5,7 +5,7 @@ import fr.acinq.bitcoin.Block
import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.channel.states.StateTestsHelperMethods
import fr.acinq.eclair.wire.{Error, Init, OpenChannel}
import fr.acinq.eclair.wire.{AcceptChannel, Error, Init, OpenChannel}
import fr.acinq.eclair.{TestConstants, TestkitBaseClass}
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
@ -86,6 +86,17 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper
}
}
test("recv OpenChannel (to_self_delay too high)") { case (bob, alice2bob, bob2alice, _) =>
within(30 seconds) {
val open = alice2bob.expectMsgType[OpenChannel]
val delayTooHigh = 10000
bob ! open.copy(toSelfDelay = delayTooHigh)
val error = bob2alice.expectMsgType[Error]
assert(error === Error(open.temporaryChannelId, ToSelfDelayTooHigh(open.temporaryChannelId, delayTooHigh, Alice.nodeParams.maxToLocalDelayBlocks).getMessage.getBytes("UTF-8")))
awaitCond(bob.stateName == CLOSED)
}
}
test("recv OpenChannel (reserve too high)") { case (bob, alice2bob, bob2alice, _) =>
within(30 seconds) {
val open = alice2bob.expectMsgType[OpenChannel]