mirror of
https://github.com/ACINQ/eclair.git
synced 2025-03-12 19:01:39 +01:00
Fix pay-to-open below minimum when using MPP (#1892)
Compare the *sum* of all pay-to-open parts against the min pay-to-open amount.
This commit is contained in:
parent
cca712571c
commit
c22094fc2d
2 changed files with 56 additions and 6 deletions
|
@ -70,9 +70,10 @@ class MultiPartPaymentFSM(nodeParams: NodeParams, paymentHash: ByteVector32, tot
|
|||
// That is why, instead, we wait for all parts to arrive. Then, if there is at least one pay-to-open part, and if
|
||||
// the total received amount is less than the minimum amount required for a pay-to-open, we fail the payment.
|
||||
updatedParts
|
||||
.collectFirst { case p: PayToOpenPart => p } match {
|
||||
case Some(p) if p.totalAmount < p.payToOpen.payToOpenMinAmount =>
|
||||
context.system.eventStream.publish(MissedPayToOpenPayment(p.paymentHash, p.totalAmount, p.payToOpen.payToOpenMinAmount))
|
||||
.collect { case p: PayToOpenPart => p.payToOpen } match {
|
||||
case payToOpenRequests if payToOpenRequests.nonEmpty && PayToOpenRequest.combine(payToOpenRequests).amountMsat < payToOpenRequests.head.payToOpenMinAmount =>
|
||||
val p = payToOpenRequests.head
|
||||
context.system.eventStream.publish(MissedPayToOpenPayment(p.paymentHash, part.totalAmount, p.payToOpenMinAmount))
|
||||
goto(PAYMENT_FAILED) using PaymentFailed(IncorrectOrUnknownPaymentDetails(part.totalAmount, nodeParams.currentBlockHeight), updatedParts)
|
||||
case _ =>
|
||||
goto(PAYMENT_SUCCEEDED) using PaymentSucceeded(updatedParts)
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
|
||||
package fr.acinq.eclair.payment
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition}
|
||||
import akka.testkit.{TestActorRef, TestProbe}
|
||||
import fr.acinq.bitcoin.ByteVector32
|
||||
import fr.acinq.bitcoin.{Block, ByteVector32}
|
||||
import fr.acinq.eclair.payment.receive.MultiPartPaymentFSM
|
||||
import fr.acinq.eclair.payment.receive.MultiPartPaymentFSM._
|
||||
import fr.acinq.eclair.wire.{IncorrectOrUnknownPaymentDetails, UpdateAddHtlc}
|
||||
import fr.acinq.eclair.{CltvExpiry, LongToBtcAmount, MilliSatoshi, NodeParams, TestConstants, TestKitBaseClass, randomBytes32, wire}
|
||||
import fr.acinq.eclair.wire.{IncorrectOrUnknownPaymentDetails, PayToOpenRequest, UpdateAddHtlc}
|
||||
import fr.acinq.eclair.{CltvExpiry, LongToBtcAmount, MilliSatoshi, NodeParams, TestConstants, TestKitBaseClass, ToMilliSatoshiConversion, randomBytes32, wire}
|
||||
import org.scalatest.funsuite.AnyFunSuiteLike
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
|
@ -110,6 +111,21 @@ class MultiPartPaymentFSMSpec extends TestKitBaseClass with AnyFunSuiteLike {
|
|||
f.eventListener.expectNoMsg(50 millis)
|
||||
}
|
||||
|
||||
test("fail all if total pay-to-open is below minimum") {
|
||||
val f = createFixture(250 millis, 20000000 msat)
|
||||
f.parent.send(f.handler, createMultiPartHtlc(20000000 msat, 16000000 msat, 1))
|
||||
val payToOpenAmount = 4000000.msat
|
||||
assert(payToOpenMinAmount > payToOpenAmount)
|
||||
f.parent.send(f.handler, createPayToOpenPart(20000000 msat, payToOpenAmount))
|
||||
val fail = f.parent.expectMsgType[MultiPartPaymentFailed]
|
||||
assert(fail.paymentHash === paymentHash)
|
||||
assert(fail.failure === IncorrectOrUnknownPaymentDetails(20000000 msat, f.currentBlockHeight))
|
||||
assert(fail.parts.length === 2)
|
||||
|
||||
f.parent.expectNoMsg(50 millis)
|
||||
f.eventListener.expectNoMsg(50 millis)
|
||||
}
|
||||
|
||||
test("fulfill all when total amount reached") {
|
||||
val f = createFixture(10 seconds, 1000 msat)
|
||||
val parts = Seq(
|
||||
|
@ -167,6 +183,22 @@ class MultiPartPaymentFSMSpec extends TestKitBaseClass with AnyFunSuiteLike {
|
|||
f.eventListener.expectNoMsg(50 millis)
|
||||
}
|
||||
|
||||
test("fulfill all if total pay-to-open is above minimum") {
|
||||
val f = createFixture(250 millis, 20000000 msat)
|
||||
val parts = Seq(
|
||||
createMultiPartHtlc(20000000 msat, 6000000 msat, 1),
|
||||
createPayToOpenPart(20000000 msat, 7000000 msat),
|
||||
createPayToOpenPart(20000000 msat, 7000000 msat)
|
||||
)
|
||||
parts.foreach(p => f.parent.send(f.handler, p))
|
||||
|
||||
val paymentResult = f.parent.expectMsgType[MultiPartPaymentSucceeded]
|
||||
assert(paymentResult.parts.toSet === parts.toSet)
|
||||
|
||||
f.parent.expectNoMsg(50 millis)
|
||||
f.eventListener.expectNoMsg(50 millis)
|
||||
}
|
||||
|
||||
test("receive additional htlcs after total amount reached") {
|
||||
val f = createFixture(10 seconds, 1000 msat)
|
||||
|
||||
|
@ -235,4 +267,21 @@ object MultiPartPaymentFSMSpec {
|
|||
HtlcPart(totalAmount, htlc)
|
||||
}
|
||||
|
||||
val payToOpenMinAmount = 10000.sat.toMilliSatoshi
|
||||
|
||||
def createPayToOpenPart(totalAmount: MilliSatoshi, payToOpenAmount: MilliSatoshi): PayToOpenPart =
|
||||
PayToOpenPart(
|
||||
totalAmount = totalAmount,
|
||||
payToOpen = PayToOpenRequest(
|
||||
chainHash = Block.RegtestGenesisBlock.blockId,
|
||||
fundingSatoshis = payToOpenAmount.truncateToSatoshi * 2,
|
||||
amountMsat = payToOpenAmount,
|
||||
payToOpenFee = 10 sat,
|
||||
paymentHash = paymentHash,
|
||||
expireAt = Long.MaxValue,
|
||||
htlc_opt = None,
|
||||
payToOpenMinAmount = payToOpenMinAmount),
|
||||
peer = ActorRef.noSender
|
||||
)
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue