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:
parent
24dadff625
commit
bb1d0e96cf
@ -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
|
||||
|
||||
|
@ -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"),
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user