@@ -25,6 +25,7 @@ import fr.acinq.eclair.TestUtils.randomTxId
25
25
import fr .acinq .eclair .channel .ChannelSpendSignature .IndividualSignature
26
26
import fr .acinq .eclair .channel ._
27
27
import fr .acinq .eclair .channel .fsm .Channel
28
+ import fr .acinq .eclair .crypto .Sphinx .HoldTime
28
29
import fr .acinq .eclair .crypto .{ShaChain , Sphinx }
29
30
import fr .acinq .eclair .payment .IncomingPaymentPacket ._
30
31
import fr .acinq .eclair .payment .OutgoingPaymentPacket ._
@@ -671,6 +672,37 @@ class PaymentPacketSpec extends AnyFunSuite with BeforeAndAfterAll {
671
672
assert(decryptedFailure == failure)
672
673
}
673
674
675
+ test(" build htlc failure onion with attribution data" ) {
676
+ // a -> b -> c -> d -> e
677
+ val recipient = ClearRecipient (e, Features .empty, finalAmount, finalExpiry, paymentSecret)
678
+ val Right (payment) = buildOutgoingPayment(TestConstants .emptyOrigin, paymentHash, Route (finalAmount, hops, None ), recipient, 1.0 )
679
+ val add_b = UpdateAddHtlc (randomBytes32(), 0 , amount_ab, paymentHash, expiry_ab, payment.cmd.onion, None , 1.0 , None )
680
+ val Right (ChannelRelayPacket (_, _, packet_c)) = decrypt(add_b, priv_b.privateKey, Features .empty)
681
+ val add_c = UpdateAddHtlc (randomBytes32(), 1 , amount_bc, paymentHash, expiry_bc, packet_c, None , 1.0 , None )
682
+ val Right (ChannelRelayPacket (_, _, packet_d)) = decrypt(add_c, priv_c.privateKey, Features .empty)
683
+ val add_d = UpdateAddHtlc (randomBytes32(), 2 , amount_cd, paymentHash, expiry_cd, packet_d, None , 1.0 , None )
684
+ val Right (ChannelRelayPacket (_, _, packet_e)) = decrypt(add_d, priv_d.privateKey, Features .empty)
685
+ val add_e = UpdateAddHtlc (randomBytes32(), 3 , amount_de, paymentHash, expiry_de, packet_e, None , 1.0 , None )
686
+ val Right (FinalPacket (_, payload_e)) = decrypt(add_e, priv_e.privateKey, Features .empty)
687
+ assert(payload_e.isInstanceOf [FinalPayload .Standard ])
688
+
689
+ // e returns a failure
690
+ val failure = IncorrectOrUnknownPaymentDetails (finalAmount, BlockHeight (currentBlockCount))
691
+ val Right (fail_e : UpdateFailHtlc ) = buildHtlcFailure(priv_e.privateKey, useAttributableFailures = true , CMD_FAIL_HTLC (add_e.id, FailureReason .LocalFailure (failure), TimestampMilli (60 )), add_e, now = TimestampMilli (62 ))
692
+ assert(fail_e.id == add_e.id)
693
+ val Right (fail_d : UpdateFailHtlc ) = buildHtlcFailure(priv_d.privateKey, useAttributableFailures = true , CMD_FAIL_HTLC (add_d.id, FailureReason .EncryptedDownstreamFailure (fail_e.reason, fail_e.attribution_opt), TimestampMilli (25 )), add_d, now = TimestampMilli (63 ))
694
+ assert(fail_d.id == add_d.id)
695
+ val Right (fail_c : UpdateFailHtlc ) = buildHtlcFailure(priv_c.privateKey, useAttributableFailures = true , CMD_FAIL_HTLC (add_c.id, FailureReason .EncryptedDownstreamFailure (fail_d.reason, fail_d.attribution_opt), TimestampMilli (10 )), add_c, now = TimestampMilli (70 ))
696
+ assert(fail_c.id == add_c.id)
697
+ val Right (fail_b : UpdateFailHtlc ) = buildHtlcFailure(priv_b.privateKey, useAttributableFailures = true , CMD_FAIL_HTLC (add_b.id, FailureReason .EncryptedDownstreamFailure (fail_c.reason, fail_c.attribution_opt), TimestampMilli (0 )), add_b, now = TimestampMilli (76 ))
698
+ assert(fail_b.id == add_b.id)
699
+ val htlcFailure = Sphinx .FailurePacket .decrypt(fail_b.reason, fail_b.attribution_opt, payment.sharedSecrets)
700
+ assert(htlcFailure.holdTimes == Seq (HoldTime (76 milliseconds, b), HoldTime (60 milliseconds, c), HoldTime (38 milliseconds, d), HoldTime (2 milliseconds, e)))
701
+ val Right (Sphinx .DecryptedFailurePacket (failingNode, decryptedFailure)) = htlcFailure.failure
702
+ assert(failingNode == e)
703
+ assert(decryptedFailure == failure)
704
+ }
705
+
674
706
test(" build htlc failure onion (blinded payment)" ) {
675
707
// a -> b -> c -> d -> e, blinded after c
676
708
val (_, route, recipient) = longBlindedHops(hex " 0451 " )
0 commit comments