1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-24 06:47:46 +01:00

MPP: don't retry if failure comes from final recipient (#1246)

This commit is contained in:
Bastien Teinturier 2019-12-11 13:53:53 +01:00 committed by GitHub
parent 6ffd35f8a7
commit 27a68a4898
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 20 additions and 7 deletions

View file

@ -249,11 +249,8 @@ class MultiPartPaymentLifecycle(nodeParams: NodeParams, cfg: SendPaymentConfig,
}
def handleChildFailure(pf: PaymentFailed, d: PaymentProgress): Option[PaymentAborted] = {
val paymentTimedOut = pf.failures.exists {
case f: RemoteFailure => f.e.failureMessage == PaymentTimeout
case _ => false
}
if (paymentTimedOut) {
val isFromFinalRecipient = pf.failures.collectFirst { case f: RemoteFailure if f.e.originNode == d.request.targetNodeId => true }.isDefined
if (isFromFinalRecipient) {
Some(PaymentAborted(d.sender, d.request, d.failures ++ pf.failures, d.pending.keySet - pf.id))
} else if (d.remainingAttempts == 0) {
val failure = LocalFailure(RetryExhausted)

View file

@ -334,8 +334,11 @@ class MultiPartHandlerSpec extends TestKit(ActorSystem("test")) with fixture.Fun
f.sender.send(handler, GetPendingPayments)
assert(f.sender.expectMsgType[PendingPayments].paymentHashes.nonEmpty)
f.commandBuffer.expectMsg(CommandBuffer.CommandSend(ByteVector32.One, 0, CMD_FAIL_HTLC(0, Right(PaymentTimeout), commit = true)))
f.commandBuffer.expectMsg(CommandBuffer.CommandSend(ByteVector32.One, 1, CMD_FAIL_HTLC(1, Right(PaymentTimeout), commit = true)))
val commands = f.commandBuffer.expectMsgType[CommandBuffer.CommandSend] :: f.commandBuffer.expectMsgType[CommandBuffer.CommandSend] :: Nil
assert(commands.toSet === Set(
CommandBuffer.CommandSend(ByteVector32.One, 0, CMD_FAIL_HTLC(0, Right(PaymentTimeout), commit = true)),
CommandBuffer.CommandSend(ByteVector32.One, 1, CMD_FAIL_HTLC(1, Right(PaymentTimeout), commit = true))
))
awaitCond({
f.sender.send(handler, GetPendingPayments)
f.sender.expectMsgType[PendingPayments].paymentHashes.isEmpty

View file

@ -403,6 +403,19 @@ class MultiPartPaymentLifecycleSpec extends TestKit(ActorSystem("test")) with fi
awaitCond(payFsm.stateName === PAYMENT_ABORTED)
}
test("failure received from final recipient") { f =>
import f._
val payment = SendMultiPartPayment(paymentHash, randomBytes32, e, 3000 * 1000 msat, expiry, 5)
initPayment(f, payment, emptyStats.copy(capacity = Stats(Seq(1000), d => Satoshi(d.toLong))), localChannels())
waitUntilAmountSent(f, payment.totalAmount)
val (childId1, _) = payFsm.stateData.asInstanceOf[PaymentProgress].pending.head
// If we receive a failure from the final node, we directly abort the payment instead of retrying.
childPayFsm.send(payFsm, PaymentFailed(childId1, paymentHash, RemoteFailure(Nil, Sphinx.DecryptedFailurePacket(e, IncorrectOrUnknownPaymentDetails(3000 * 1000 msat, 42))) :: Nil))
relayer.expectNoMsg(50 millis)
awaitCond(payFsm.stateName === PAYMENT_ABORTED)
}
test("fail after too many attempts") { f =>
import f._
val payment = SendMultiPartPayment(paymentHash, randomBytes32, e, 3000 * 1000 msat, expiry, 2)